MDL-69166 core_payment: archiving accounts, triggering events, tests

This commit is contained in:
Marina Glancy 2020-10-02 16:01:56 +10:00 committed by Shamim Rezaie
parent 73527fa213
commit 409857a42f
18 changed files with 893 additions and 21 deletions

View File

@ -284,6 +284,12 @@ if ($hassiteconfig) {
$ADMIN->add('modules', new admin_category('paymentgateways', new lang_string('type_pg_plural', 'plugin')));
$temp = new admin_settingpage('managepaymentgateways', new lang_string('type_pgmanage', 'plugin'));
$temp->add(new \core_admin\local\settings\manage_payment_gateway_plugins());
$temp->add(new admin_setting_description(
'managepaymentgatewayspostfix',
'',
new lang_string('gotopaymentaccounts', 'payment',
html_writer::link(new moodle_url('/payment/accounts.php'), get_string('paymentaccounts', 'payment')))
));
$ADMIN->add('paymentgateways', $temp);
$plugins = core_plugin_manager::instance()->get_plugins_of_type('pg');

View File

@ -303,9 +303,16 @@ class enrol_fee_plugin extends enrol_plugin {
$mform->addElement('select', 'status', get_string('status', 'enrol_fee'), $options);
$mform->setDefault('status', $this->get_config('status'));
$mform->addElement('select', 'customint1', get_string('paymentaccount', 'payment'),
['' => ''] + \core_payment\helper::get_payment_accounts_menu($context));
$mform->addRule('customint1', get_string('required'), 'required', null, 'client');
$accounts = \core_payment\helper::get_payment_accounts_menu($context);
if ($accounts) {
$accounts = ((count($accounts) > 1) ? ['' => ''] : []) + $accounts;
$mform->addElement('select', 'customint1', get_string('paymentaccount', 'payment'), $accounts);
} else {
$mform->addElement('static', 'customint1_text', get_string('paymentaccount', 'payment'),
html_writer::span(get_string('noaccountsavilable', 'payment'), 'alert alert-danger'));
$mform->addElement('hidden', 'customint1');
$mform->setType('customint1', PARAM_INT);
}
$mform->addElement('text', 'cost', get_string('cost', 'enrol_fee'), array('size' => 4));
$mform->setType('cost', PARAM_RAW);
@ -379,6 +386,12 @@ class enrol_fee_plugin extends enrol_plugin {
$typeerrors = $this->validate_param_types($data, $tovalidate);
$errors = array_merge($errors, $typeerrors);
if ($data['status'] == ENROL_INSTANCE_ENABLED &&
(!$data['customint1']
|| !array_key_exists($data['customint1'], \core_payment\helper::get_payment_accounts_menu($context)))) {
$errors['status'] = 'Enrolments can not be enabled without specifying the payment account';
}
return $errors;
}

View File

@ -0,0 +1,46 @@
@enrol @enrol_fee
Feature: Signing up for a course with a fee enrolment method
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| teacher1 | Teacher | 1 | teacher1@example.com |
| student1 | Student | 1 | student1@example.com |
| manager1 | Manager | 1 | manager1@example.com |
And the following "courses" exist:
| fullname | shortname | format | summary |
| Course 1 | C1 | topics | |
And the following "course enrolments" exist:
| user | course | role |
| teacher1 | C1 | editingteacher |
| manager1 | C1 | manager |
And payment plugin "paypal" is enabled
And the following "core_payment > payment accounts" exist:
| name | gateways |
| Account1 | paypal |
And I log in as "admin"
And I navigate to "Plugins > Enrolments > Manage enrol plugins" in site administration
And I click on "Enable" "link" in the "Fee" "table_row"
And I log out
@javascript
Scenario: Student can see the payment prompt on the course enrolment page
When I log in as "manager1"
And I am on "Course 1" course homepage
And I navigate to "Users > Enrolment methods" in current page administration
And I select "Fee" from the "Add method" singleselect
And I set the following fields to these values:
| Payment account | Account1 |
| Enrol cost | 10 |
| Currency | Euro |
And I press "Add method"
And I log out
And I log in as "student1"
And I am on course index
And I follow "Course 1"
And I should see "This course requires a payment for entry."
#And I should see "Cost: EUR 10.00" # TODO for some reason behat does not "see" this text.
And I press "Pay enrolment fee"
And I should see "PayPal" in the "Select Payment Type" "dialogue"
And I click on "Cancel" "button" in the "Select Payment Type" "dialogue"
And I log out

View File

@ -22,22 +22,34 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['accountarchived'] = 'Archived';
$string['accountdeleteconfirm'] = 'If this account has previous payments, they will be archived, otherwise all other data will be permanently deleted. Are you sure you want to continue?';
$string['accountname'] = 'Account name';
$string['accountnotavailable'] = 'Not available';
$string['callbacknotimplemented'] = 'The callback is not implemented for component {$a}.';
$string['createaccount'] = 'Create payment account';
$string['deleteorarchive'] = 'Delete or archive';
$string['eventaccountcreated'] = 'Payment account created';
$string['eventaccountdeleted'] = 'Payment account deleted';
$string['eventaccountupdated'] = 'Payment account updated';
$string['feeincludesurcharge'] = '{$a->fee} (includes {$a->surcharge}% surcharge for using this payment type)';
$string['gatewaycannotbeenabled'] = 'The payment gateway cannot be enabled because the configuration is incomplete.';
$string['gatewaydisabled'] = 'Disabled';
$string['gatewayenabled'] = 'Enabled';
$string['gatewaynotfound'] = 'Gateway not found';
$string['gotomanageplugins'] = 'Enable and disable payment gateways and set surcharges via {$a}.';
$string['gotopaymentaccounts'] = 'You can create multiple payment accounts using any of these gateways on the {$a} page';
$string['hidearchived'] = 'Hide archived';
$string['noaccountsavilable'] = 'No payment accounts are available.';
$string['nocurrencysupported'] = 'No payment in any currency is supported. Please make sure that at least one payment gateway is enabled.';
$string['nogateway'] = 'There is no payment gateway that can be used.';
$string['nogatewayselected'] = 'You first need to select a payment gateway.';
$string['payments'] = 'Payments';
$string['paymentaccount'] = 'Payment account';
$string['paymentaccounts'] = 'Payment accounts';
$string['restoreaccount'] = 'Restore';
$string['selectpaymenttype'] = 'Select payment type';
$string['showarchived'] = 'Show archived';
$string['supportedcurrencies'] = 'Supported currencies';
$string['surcharge'] = 'Surcharge (percentage)';
$string['surcharge_desc'] = 'The surcharge is an additional percentage charged to users who choose to pay using this payment gateway.';

View File

@ -4295,6 +4295,7 @@
<FIELD NAME="idnumber" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="enabled" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="archived" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>

View File

@ -2959,5 +2959,20 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2021052500.33);
}
if ($oldversion < 2021052500.26) {
// Define field archived to be added to payment_accounts.
$table = new xmldb_table('payment_accounts');
$field = new xmldb_field('archived', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'enabled');
// Conditionally launch add field archived.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2021052500.26);
}
return true;
}

View File

@ -25,6 +25,8 @@
require_once(__DIR__ . '/../config.php');
require_once($CFG->libdir . '/adminlib.php');
$showarchived = optional_param('showarchived', false, PARAM_BOOL);
admin_externalpage_setup('paymentaccounts');
$PAGE->set_heading(get_string('paymentaccounts', 'payment'));
@ -32,7 +34,7 @@ $enabledplugins = \core\plugininfo\pg::get_enabled_plugins();
echo $OUTPUT->header();
$accounts = \core_payment\helper::get_payment_accounts_to_manage(context_system::instance());
$accounts = \core_payment\helper::get_payment_accounts_to_manage(context_system::instance(), $showarchived);
$table = new html_table();
$table->head = [get_string('accountname', 'payment'), get_string('type_pg', 'plugin'), ''];
$table->colclasses = ['', '', 'mdl-right'];
@ -50,15 +52,23 @@ foreach ($accounts as $account) {
if (!$account->is_available()) {
$name .= ' ' . html_writer::span(get_string('accountnotavailable', 'payment'), 'badge badge-warning');
}
if ($account->get('archived')) {
$name .= ' ' . html_writer::span(get_string('accountarchived', 'payment'), 'badge badge-secondary');
}
$menu = new action_menu();
$menu->set_alignment(action_menu::TL, action_menu::BL);
$menu->set_menu_trigger(get_string('edit'));
if ($canmanage) {
$menu->add(new action_menu_link_secondary($account->get_edit_url(), null, get_string('edit')));
$deleteurl = $account->get_edit_url(['delete' => 1, 'sesskey' => sesskey()]);
$deleteaction = new confirm_action(get_string('deleteconfirm', 'tool_recyclebin'));
$menu->add(new action_menu_link_secondary($deleteurl, null, get_string('delete')));
if (!$account->get('archived')) {
$deleteurl = $account->get_edit_url(['delete' => 1, 'sesskey' => sesskey()]);
$menu->add(new action_menu_link_secondary($deleteurl, null, get_string('deleteorarchive', 'payment'),
['data-action' => 'delete']));
} else {
$restoreurl = $account->get_edit_url(['restore' => 1, 'sesskey' => sesskey()]);
$menu->add(new action_menu_link_secondary($restoreurl, null, get_string('restoreaccount', 'payment')));
}
}
$table->data[] = [$name, join(', ', $gateways), $OUTPUT->render($menu)];
@ -66,6 +76,20 @@ foreach ($accounts as $account) {
echo html_writer::table($table);
$PAGE->requires->event_handler('[data-action=delete]', 'click', 'M.util.show_confirm_dialog',
array('message' => get_string('accountdeleteconfirm', 'payment')));
echo html_writer::div(html_writer::link(new moodle_url($PAGE->url, ['showarchived' => !$showarchived]),
$showarchived ? get_string('hidearchived', 'payment') : get_string('showarchived', 'payment')), 'mdl-right');
echo $OUTPUT->single_button(new moodle_url('/payment/manage_account.php'), get_string('createaccount', 'payment'), 'get');
if (has_capability('moodle/site:config', context_system::instance())) {
// For administrators add a link to "Manage payment gateways" page.
$link = html_writer::link(new moodle_url('/admin/settings.php', ['section' => 'managepaymentgateways']),
get_string('type_pgmanage', 'plugin'));
$text = get_string('gotomanageplugins', 'payment', $link);
echo html_writer::div($text, 'pt-3');
}
echo $OUTPUT->footer();

View File

@ -65,6 +65,10 @@ class account extends persistent {
'type' => PARAM_BOOL,
'default' => true
],
'archived' => [
'type' => PARAM_BOOL,
'default' => false
],
);
}

View File

@ -0,0 +1,94 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_payment\event;
use core\event\base;
use core_payment\account;
/**
* Class account_created
*
* @package core_payment
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Class account_created
*
* @package core_payment
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class account_created extends base {
/**
* Initialise event parameters.
*/
protected function init() {
$this->data['objecttable'] = 'payment_accounts';
$this->data['crud'] = 'c';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Create an instance of the event and add a record snapshot
*
* @param account $account
* @return base
* @throws \coding_exception
*/
public static function create_from_account(account $account) {
$eventparams = [
'objectid' => $account->get('id'),
'context' => $account->get_context(),
'other' => ['name' => $account->get('name')]
];
$event = self::create($eventparams);
$event->add_record_snapshot($event->objecttable, $account->to_record());
return $event;
}
/**
* Returns localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventaccountcreated', 'payment');
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
$name = s($this->other['name']);
return "The user with id '$this->userid' created payment account with id '$this->objectid' and the name '{$name}'.";
}
/**
* Returns relevant URL.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/payment/accounts.php');
}
}

View File

@ -0,0 +1,94 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_payment\event;
use core\event\base;
use core_payment\account;
/**
* Class account_deleted
*
* @package core_payment
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Class account_deleted
*
* @package core_payment
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class account_deleted extends base {
/**
* Initialise event parameters.
*/
protected function init() {
$this->data['objecttable'] = 'payment_accounts';
$this->data['crud'] = 'd';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Create an instance of the event and add a record snapshot
*
* @param account $account
* @return base
* @throws \coding_exception
*/
public static function create_from_account(account $account) {
$eventparams = [
'objectid' => $account->get('id'),
'context' => $account->get_context(),
'other' => ['name' => $account->get('name')]
];
$event = self::create($eventparams);
$event->add_record_snapshot($event->objecttable, $account->to_record());
return $event;
}
/**
* Returns localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventaccountdeleted', 'payment');
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
$name = s($this->other['name']);
return "The user with id '$this->userid' deleted payment account with id '$this->objectid' and the name '{$name}'.";
}
/**
* Returns relevant URL.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/payment/accounts.php');
}
}

View File

@ -0,0 +1,101 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core_payment\event;
use core\event\base;
use core_payment\account;
/**
* Class account_updated
*
* @package core_payment
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Class account_updated
*
* @package core_payment
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class account_updated extends base {
/**
* Initialise event parameters.
*/
protected function init() {
$this->data['objecttable'] = 'payment_accounts';
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_OTHER;
}
/**
* Create an instance of the event and add a record snapshot
*
* @param account $account
* @param array $other
* @return base
*/
public static function create_from_account(account $account, array $other = []) {
$eventparams = [
'objectid' => $account->get('id'),
'context' => $account->get_context(),
'other' => ['name' => $account->get('name')] + $other
];
$event = self::create($eventparams);
$event->add_record_snapshot($event->objecttable, $account->to_record());
return $event;
}
/**
* Returns localised event name.
*
* @return string
*/
public static function get_name() {
return get_string('eventaccountupdated', 'payment');
}
/**
* Returns non-localised event description with id's for admin use only.
*
* @return string
*/
public function get_description() {
$name = s($this->other['name']);
if (!empty($this->other['archived'])) {
$verb = 'archived';
} else if (!empty($this->other['restored'])) {
$verb = 'restored';
} else {
$verb = 'updated';
}
return "The user with id '$this->userid' $verb payment account with id '$this->objectid' and the name '{$name}'.";
}
/**
* Returns relevant URL.
*
* @return \moodle_url
*/
public function get_url() {
return new \moodle_url('/payment/accounts.php');
}
}

View File

@ -24,6 +24,10 @@
namespace core_payment;
use core_payment\event\account_created;
use core_payment\event\account_deleted;
use core_payment\event\account_updated;
defined('MOODLE_INTERNAL') || die();
/**
@ -266,18 +270,22 @@ class helper {
* Save a new or edited payment account (used in management interface)
*
* @param \stdClass $data
* @return account
*/
public static function save_payment_account(\stdClass $data) {
public static function save_payment_account(\stdClass $data): account {
if (empty($data->id)) {
$account = new account(0, $data);
$account->save();
account_created::create_from_account($account)->trigger();
} else {
$account = new account($data->id);
$account->from_record($data);
$account->save();
account_updated::create_from_account($account)->trigger();
}
$account->save();
// TODO trigger event.
return $account;
}
/**
@ -285,32 +293,60 @@ class helper {
*
* @param account $account
*/
public static function delete_payment_account(account $account) {
public static function delete_payment_account(account $account): void {
global $DB;
if ($DB->record_exists('payments', ['accountid' => $account->get('id')])) {
$account->set('archived', 1);
$account->save();
account_updated::create_from_account($account, ['archived' => 1])->trigger();
return;
}
foreach ($account->get_gateways(false) as $gateway) {
if ($gateway->get('id')) {
$gateway->delete();
}
}
$event = account_deleted::create_from_account($account);
$account->delete();
// TODO trigger event.
$event->trigger();
}
/**
* Restore archived payment account (used in management interface)
*
* @param account $account
*/
public static function restore_payment_account(account $account): void {
$account->set('archived', 0);
$account->save();
account_updated::create_from_account($account, ['restored' => 1])->trigger();
}
/**
* Save a payment gateway linked to an existing account (used in management interface)
*
* @param \stdClass $data
* @return account_gateway
*/
public static function save_payment_gateway(\stdClass $data) {
public static function save_payment_gateway(\stdClass $data): account_gateway {
if (empty($data->id)) {
$gateway = new account_gateway(0, $data);
$records = account_gateway::get_records(['accountid' => $data->accountid, 'gateway' => $data->gateway]);
if ($records) {
$gateway = reset($records);
} else {
$gateway = new account_gateway(0, $data);
}
} else {
$gateway = new account_gateway($data->id);
unset($data->accountid, $data->gateway, $data->id);
$gateway->from_record($data);
}
unset($data->accountid, $data->gateway, $data->id);
$gateway->from_record($data);
$account = $gateway->get_account();
$gateway->save();
// TODO trigger event.
account_updated::create_from_account($account)->trigger();
return $gateway;
}
/**
@ -319,8 +355,10 @@ class helper {
* @param \context $context
* @return account[]
*/
public static function get_payment_accounts_to_manage(\context $context): array {
return account::get_records(['contextid' => $context->id]);
public static function get_payment_accounts_to_manage(\context $context, bool $showarchived = false): array {
$records = account::get_records(['contextid' => $context->id] + ($showarchived ? [] : ['archived' => 0]));
\core_collator::asort_objects_by_method($records, 'get_formatted_name');
return $records;
}
/**
@ -333,7 +371,7 @@ class helper {
global $DB;
[$sql, $params] = $DB->get_in_or_equal($context->get_parent_context_ids(true));
$accounts = array_filter(account::get_records_select('contextid '.$sql, $params), function($account) {
return $account->is_available();
return $account->is_available() && !$account->get('archived');
});
return array_map(function($account) {
return $account->get_formatted_name();

View File

@ -27,6 +27,7 @@ require_once($CFG->libdir . '/adminlib.php');
$id = optional_param('id', 0, PARAM_INT);
$delete = optional_param('delete', false, PARAM_BOOL);
$restore = optional_param('restore', false, PARAM_BOOL);
$pageurl = new moodle_url('/payment/manage_account.php');
admin_externalpage_setup('paymentaccounts', '', [], $pageurl);
@ -36,10 +37,14 @@ $enabledplugins = \core\plugininfo\pg::get_enabled_plugins();
$account = new \core_payment\account($id);
require_capability('moodle/payment:manageaccounts', $account->get_context());
if ($delete && confirm_sesskey()) {
if ($delete && !$account->get('archived') && confirm_sesskey()) {
\core_payment\helper::delete_payment_account($account);
redirect(new moodle_url('/payment/accounts.php'));
}
if ($restore && $account->get('archived') && confirm_sesskey()) {
\core_payment\helper::restore_payment_account($account);
redirect(new moodle_url('/payment/accounts.php'));
}
$PAGE->set_heading($id ? format_string($account->get('name')) : get_string('createaccount', 'payment'));

View File

@ -0,0 +1,130 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Testing accounts management in payments API
*
* @package core_payment
* @category test
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_payment;
use advanced_testcase;
use core\plugininfo\pg;
/**
* Testing accounts management in payments API
*
* @package core_payment
* @category test
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class accounts_testcase extends advanced_testcase {
protected function enable_paypal_gateway(): bool {
if (!array_key_exists('paypal', \core_component::get_plugin_list('pg'))) {
return false;
}
pg::set_enabled_plugins('paypal');
return true;
}
public function test_create_account() {
global $DB;
$this->resetAfterTest();
$account = helper::save_payment_account((object)['name' => 'Test 1', 'idnumber' => '']);
$this->assertNotEmpty($account->get('id'));
$this->assertEquals('Test 1', $DB->get_field('payment_accounts', 'name', ['id' => $account->get('id')]));
}
public function test_update_account_details() {
global $DB;
$this->resetAfterTest();
$account = helper::save_payment_account((object)['name' => 'Test 1', 'idnumber' => '']);
$record = $account->to_record();
$record->name = 'Edited name';
$editedaccount = helper::save_payment_account($record);
$this->assertEquals($account->get('id'), $editedaccount->get('id'));
$this->assertEquals('Edited name', $DB->get_field('payment_accounts', 'name', ['id' => $account->get('id')]));
}
public function test_update_account_gateways() {
global $DB;
if (!$this->enable_paypal_gateway()) {
$this->markTestSkipped('Paypal payment gateway plugin not found');
}
$this->resetAfterTest();
$account = helper::save_payment_account((object)['name' => 'Test 1', 'idnumber' => '']);
$gateway = helper::save_payment_gateway(
(object)['accountid' => $account->get('id'), 'gateway' => 'paypal', 'config' => 'T1']);
$this->assertNotEmpty($gateway->get('id'));
$this->assertEquals('T1', $DB->get_field('payment_gateways', 'config', ['id' => $gateway->get('id')]));
// Update by id.
$editedgateway = helper::save_payment_gateway(
(object)['id' => $gateway->get('id'), 'accountid' => $account->get('id'), 'gateway' => 'paypal', 'config' => 'T2']);
$this->assertEquals($gateway->get('id'), $editedgateway->get('id'));
$this->assertEquals('T2', $DB->get_field('payment_gateways', 'config', ['id' => $gateway->get('id')]));
// Update by account/gateway.
$editedgateway = helper::save_payment_gateway(
(object)['accountid' => $account->get('id'), 'gateway' => 'paypal', 'config' => 'T3']);
$this->assertEquals($gateway->get('id'), $editedgateway->get('id'));
$this->assertEquals('T3', $DB->get_field('payment_gateways', 'config', ['id' => $gateway->get('id')]));
}
public function test_delete_account() {
global $DB;
if (!$this->enable_paypal_gateway()) {
$this->markTestSkipped('Paypal payment gateway plugin not found');
}
$this->resetAfterTest();
// Delete account without payments, it will be deleted, gateways will also be deleted.
$account = helper::save_payment_account((object)['name' => 'Test 1', 'idnumber' => '']);
$gateway = helper::save_payment_gateway(
(object)['accountid' => $account->get('id'), 'gateway' => 'paypal', 'config' => 'T1']);
helper::delete_payment_account(account::get_record(['id' => $account->get('id')]));
$this->assertEmpty($DB->get_records('payment_accounts', ['id' => $account->get('id')]));
$this->assertEmpty($DB->get_records('payment_gateways', ['id' => $gateway->get('id')]));
}
public function test_archive_restore_account() {
global $DB, $USER;
$this->resetAfterTest();
// Delete account with payments - it will be archived.
$this->setAdminUser();
$account = helper::save_payment_account((object)['name' => 'Test 1', 'idnumber' => '']);
$DB->insert_record('payments', ['accountid' => $account->get('id'), 'component' => 'test', 'componentid' => 1,
'userid' => $USER->id]);
helper::delete_payment_account(account::get_record(['id' => $account->get('id')]));
$this->assertEquals(1, $DB->get_field('payment_accounts', 'archived', ['id' => $account->get('id')]));
// Restore account.
helper::restore_payment_account(account::get_record(['id' => $account->get('id')]));
$this->assertEquals(0, $DB->get_field('payment_accounts', 'archived', ['id' => $account->get('id')]));
}
}

View File

@ -0,0 +1,87 @@
@core @core_payment
Feature: Manage payment accounts
@javascript
Scenario: Creating and editing payment account
When I log in as "admin"
And I navigate to "Payments > Payment accounts" in site administration
And I follow "Manage payment gateways"
And I click on "Enable" "link" in the "PayPal" "table_row"
And I follow "Payment accounts"
And I press "Create payment account"
And I set the field "Account name" to "TestAccount"
And I press "Save changes"
And I should see "PayPal" in the "TestAccount" "table_row"
And I open the action menu in "TestAccount" "table_row"
And I choose "Edit" in the open action menu
And I set the field "Account name" to "NewName"
And I press "Save changes"
And I should see "PayPal" in the "NewName" "table_row"
And I should not see "TestAccount"
And I log out
@javascript
Scenario: Configuring gateways on payment accounts
Given payment plugin "paypal" is enabled
And the following "core_payment > payment accounts" exist:
| name |
| Account1 |
| Account2 |
When I log in as "admin"
And I navigate to "Payments > Payment accounts" in site administration
Then I should see "Not available" in the "Account1" "table_row"
And I click on "PayPal" "link" in the "Account1" "table_row"
And I set the field "Brand name" to "Test paypal"
And I set the following fields to these values:
| Brand name | Test paypal |
| Client ID | Test |
| Secret | Test |
| Enable | 1 |
And I press "Save changes"
And I should see "PayPal" in the "Account1" "table_row"
And I should not see "Not available" in the "Account1" "table_row"
And I log out
@javascript
Scenario: Deleting payment accounts
Given payment plugin "paypal" is enabled
And the following "core_payment > payment accounts" exist:
| name |
| Account1 |
| Account2 |
When I log in as "admin"
And I navigate to "Payments > Payment accounts" in site administration
And I open the action menu in "Account1" "table_row"
And I choose "Delete or archive" in the open action menu
And I click on "Yes" "button" in the "Confirmation" "dialogue"
Then I should not see "Account1"
And I should see "Account2"
And I log out
@javascript
Scenario: Archiving and restoring accounts
Given payment plugin "paypal" is enabled
And the following "users" exist:
| username |
| user1 |
And the following "core_payment > payment accounts" exist:
| name |
| Account1 |
| Account2 |
And the following "core_payment > payments" exist:
| account | component | amount | user |
| Account1 | test | 10 | user1 |
| Account1 | test | 15 | user1 |
When I log in as "admin"
And I navigate to "Payments > Payment accounts" in site administration
And I open the action menu in "Account1" "table_row"
And I choose "Delete or archive" in the open action menu
And I click on "Yes" "button" in the "Confirmation" "dialogue"
Then I should not see "Account1"
And I should see "Account2"
And I follow "Show archived"
And I should see "Archived" in the "Account1" "table_row"
And I open the action menu in "Account1" "table_row"
And I choose "Restore" in the open action menu
And I should not see "Archived" in the "Account1" "table_row"
And I log out

View File

@ -0,0 +1,52 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Steps definitions related with the payment API
*
* @package core_payment
* @category test
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
use Behat\Gherkin\Node\TableNode as TableNode,
Behat\Mink\Exception\ExpectationException as ExpectationException,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
/**
* Steps definitions related with the payment API
*
* @package core_payment
* @category test
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_payment extends behat_question_base {
/**
* Enable payment plugin
*
* @Given /^payment plugin "(?P<plugin_name_string>(?:[^"]|\\")*)" is enabled$/
* @param string $pluginname
*/
public function payment_plugin_is_enabled($pluginname) {
$plugins = \core\plugininfo\pg::get_enabled_plugins();
\core\plugininfo\pg::set_enabled_plugins(array_merge($plugins, [$pluginname]));
}
}

View File

@ -0,0 +1,69 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Behat data generator for core_payment.
*
* @package core_payment
* @category test
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Behat data generator for core_payment.
*
* @package core_payment
* @category test
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_core_payment_generator extends behat_generator_base {
protected function get_creatable_entities(): array {
return [
'payment accounts' => [
'singular' => 'payment account',
'datagenerator' => 'payment_account',
'required' => ['name'],
],
'payments' => [
'singular' => 'payment',
'datagenerator' => 'payment',
'required' => ['account', 'amount', 'user'],
'switchids' => ['account' => 'accountid', 'user' => 'userid'],
],
];
}
/**
* Look up the id of a account from its name.
*
* @param string $accountname
* @return int corresponding id.
*/
protected function get_account_id(string $accountname): int {
global $DB;
if (!$id = $DB->get_field('payment_accounts', 'id', ['name' => $accountname])) {
throw new Exception('There is no account with name "' . $accountname . '".');
}
return $id;
}
}

View File

@ -0,0 +1,81 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* Quiz module test data generator class
*
* @package core_payment
* @category test
* @copyright 2020 Marina Glancy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class core_payment_generator extends component_generator_base {
protected $accountcounter = 0;
/**
* Create a payment account
*
* @param array $data account data (name, idnumber, enabled) and additionally field 'gateways' that can include
* a list of gateways that should be mock-enabled for this account.
*/
public function create_payment_account(array $data): \core_payment\account {
$this->accountcounter++;
$gateways = [];
if (!empty($data['gateways'])) {
$gateways = preg_split('/,/', $data['gateways']);
}
unset($data['gateways']);
$account = \core_payment\helper::save_payment_account(
(object)($data + ['name' => 'Test '.$this->accountcounter, 'idnumber' => '', 'enabled' => 1]));
foreach ($gateways as $gateway) {
\core_payment\helper::save_payment_gateway(
(object)['accountid' => $account->get('id'), 'gateway' => $gateway, 'enabled' => 1]);
}
return $account;
}
/**
* Create a payment account
*
* @param array $data
*/
public function create_payment(array $data): int {
global $DB;
if (empty($data['accountid']) || !\core_payment\account::get_record(['id' => $data['accountid']])) {
throw new coding_exception('Account id is not specified or does not exist');
}
if (empty($data['amount'])) {
throw new coding_exception('Amount must be specified');
}
$gateways = \core\plugininfo\pg::get_enabled_plugins();
if (empty($data['gateway'])) {
$data['gateway'] = reset($gateways);
}
$id = $DB->insert_record('payments', $data +
['component' => 'testcomponent',
'componentarea' => 'teatarea',
'componentid' => 0,
'currency' => 'AUD']);
return $id;
}
}