View | Details | Raw Unified | Return to bug 26355
Collapse All | Expand All

(-)a/Koha/REST/V1/Patrons/SelfRenewal.pm (-7 / +8 lines)
Lines 42-51 Controller function that retrieves the metadata required to begin a patron self Link Here
42
sub start {
42
sub start {
43
    my $c = shift->openapi->valid_input or return;
43
    my $c = shift->openapi->valid_input or return;
44
44
45
    my $patron = $c->stash('koha.user');
45
    my $patron = Koha::Patrons->find( $c->param('patron_id') );
46
46
47
    return $c->render( status => 403, openapi => { error => "You are not eligible for self-renewal" } )
47
    return $c->render( status => 403, openapi => { error => "You are not eligible for self-renewal" } )
48
        if !$patron->is_eligible_for_self_renewal();
48
        if !$patron->is_eligible_for_self_renewal() || !$patron;
49
49
50
    return try {
50
    return try {
51
        my $verification_checks = Koha::Patron::Attribute::Types->search_with_library_limits(
51
        my $verification_checks = Koha::Patron::Attribute::Types->search_with_library_limits(
Lines 88-94 Controller function that receives the renewal request and process the renewal Link Here
88
sub submit {
88
sub submit {
89
    my $c = shift->openapi->valid_input or return;
89
    my $c = shift->openapi->valid_input or return;
90
90
91
    my $patron = $c->stash('koha.user');
91
    my $patron = Koha::Patrons->find( $c->param('patron_id') );
92
92
93
    return try {
93
    return try {
94
        Koha::Database->new->schema->txn_do(
94
        Koha::Database->new->schema->txn_do(
Lines 96-110 sub submit { Link Here
96
                my $body = $c->req->json;
96
                my $body = $c->req->json;
97
97
98
                return $c->render( status => 403, openapi => { error => "You are not eligible for self-renewal" } )
98
                return $c->render( status => 403, openapi => { error => "You are not eligible for self-renewal" } )
99
                    if !$patron->is_eligible_for_self_renewal();
99
                    if !$patron->is_eligible_for_self_renewal() || !$patron;
100
100
101
                my $OPACPatronDetails = C4::Context->preference("OPACPatronDetails");
101
                my $OPACPatronDetails = C4::Context->preference("OPACPatronDetails");
102
                if ($OPACPatronDetails) {
102
                if ($OPACPatronDetails) {
103
                    my $extended_attributes = delete $body->{extended_attributes};
103
                    my $patron_details      = $body->{patron};
104
                    my $extended_attributes = delete $patron_details->{extended_attributes};
104
                    my $changed_fields      = {};
105
                    my $changed_fields      = {};
105
                    my $changes_detected;
106
                    my $changes_detected;
106
                    foreach my $key ( keys %$body ) {
107
                    foreach my $key ( keys %$patron_details ) {
107
                        my $submitted_value = $body->{$key};
108
                        my $submitted_value = $patron_details->{$key};
108
                        my $original_value  = $patron->$key;
109
                        my $original_value  = $patron->$key;
109
110
110
                        if ( $submitted_value ne $original_value ) {
111
                        if ( $submitted_value ne $original_value ) {
(-)a/api/v1/swagger/definitions/patron_self_renewal.yaml (-2 / +7 lines)
Lines 1-11 Link Here
1
---
1
---
2
type: "object"
2
type: "object"
3
properties:
3
properties:
4
  verification_steps:
4
  verification_checks:
5
    type:
5
    type:
6
      - array
6
      - array
7
      - "null"
7
      - "null"
8
    description: Any additional verification steps set in patron attribute types
8
    description: Any additional verification checks set in patron attribute types
9
  self_renewal_settings:
9
  self_renewal_settings:
10
    type:
10
    type:
11
      - object
11
      - object
Lines 21-23 properties: Link Here
21
      - array
21
      - array
22
      - "null"
22
      - "null"
23
    description: The method(s) through which confirmation of renewal has been sent
23
    description: The method(s) through which confirmation of renewal has been sent
24
  patron:
25
    type:
26
      - object
27
    description: The object representing the patron fields from the OPAC form
28
additionalProperties: false
(-)a/api/v1/swagger/paths/public_patrons.yaml (-1 / +3 lines)
Lines 344-350 Link Here
344
        description: Under maintenance
344
        description: Under maintenance
345
        schema:
345
        schema:
346
          $ref: "../swagger.yaml#/definitions/error"
346
          $ref: "../swagger.yaml#/definitions/error"
347
/public/patrons/self_renewal:
347
/public/patrons/{patron_id}/self_renewal:
348
  get:
348
  get:
349
    x-mojo-to: Patrons::SelfRenewal#start
349
    x-mojo-to: Patrons::SelfRenewal#start
350
    operationId: startSelfRenewal
350
    operationId: startSelfRenewal
Lines 354-359 Link Here
354
    produces:
354
    produces:
355
      - application/json
355
      - application/json
356
    parameters:
356
    parameters:
357
      - $ref: "../swagger.yaml#/parameters/patron_id_pp"
357
      - $ref: "../swagger.yaml#/parameters/match"
358
      - $ref: "../swagger.yaml#/parameters/match"
358
      - $ref: "../swagger.yaml#/parameters/order_by"
359
      - $ref: "../swagger.yaml#/parameters/order_by"
359
      - $ref: "../swagger.yaml#/parameters/page"
360
      - $ref: "../swagger.yaml#/parameters/page"
Lines 404-409 Link Here
404
    produces:
405
    produces:
405
      - application/json
406
      - application/json
406
    parameters:
407
    parameters:
408
      - $ref: "../swagger.yaml#/parameters/patron_id_pp"
407
      - description: A JSON object containing information about the self-renewal
409
      - description: A JSON object containing information about the self-renewal
408
        in: body
410
        in: body
409
        name: body
411
        name: body
(-)a/api/v1/swagger/swagger.yaml (-2 / +2 lines)
Lines 583-590 paths: Link Here
583
    $ref: "./paths/public_patrons.yaml#/~1public~1patrons~1{patron_id}~1ill~1requests"
583
    $ref: "./paths/public_patrons.yaml#/~1public~1patrons~1{patron_id}~1ill~1requests"
584
  "/public/patrons/{patron_id}/password":
584
  "/public/patrons/{patron_id}/password":
585
    $ref: "./paths/public_patrons.yaml#/~1public~1patrons~1{patron_id}~1password"
585
    $ref: "./paths/public_patrons.yaml#/~1public~1patrons~1{patron_id}~1password"
586
  "/public/patrons/self_renewal":
586
  "/public/patrons/{patron_id}/self_renewal":
587
    $ref: "./paths/public_patrons.yaml#/~1public~1patrons~1self_renewal"
587
    $ref: "./paths/public_patrons.yaml#/~1public~1patrons~1{patron_id}~1self_renewal"
588
  "/public/tickets":
588
  "/public/tickets":
589
    $ref: "./paths/tickets.yaml#/~1public~1tickets"
589
    $ref: "./paths/tickets.yaml#/~1public~1tickets"
590
  /quotes:
590
  /quotes:
(-)a/koha-tmpl/intranet-tmpl/prog/js/fetch/patron-api-client.js (-4 / +4 lines)
Lines 28-43 export class PatronAPIClient { Link Here
28
28
29
    get self_renewal() {
29
    get self_renewal() {
30
        return {
30
        return {
31
            start: (query, params, headers) =>
31
            start: (id, query, params, headers) =>
32
                this.httpClient.getAll({
32
                this.httpClient.getAll({
33
                    endpoint: "public/patrons/self_renewal",
33
                    endpoint: `public/patrons/${id}/self_renewal`,
34
                    query,
34
                    query,
35
                    params,
35
                    params,
36
                    headers,
36
                    headers,
37
                }),
37
                }),
38
            submit: renewal =>
38
            submit: (id, renewal) =>
39
                this.httpClient.post({
39
                this.httpClient.post({
40
                    endpoint: "public/patrons/self_renewal",
40
                    endpoint: `public/patrons/${id}/self_renewal`,
41
                    body: renewal,
41
                    body: renewal,
42
                }),
42
                }),
43
        };
43
        };
(-)a/koha-tmpl/intranet-tmpl/prog/js/vue/components/Islands/PatronSelfRenewal/PatronSelfRenewal.vue (-2 / +5 lines)
Lines 80-85 import { $__ } from "@koha-vue/i18n"; Link Here
80
80
81
export default {
81
export default {
82
    components: { VerificationChecks },
82
    components: { VerificationChecks },
83
    props: {
84
        patron: String,
85
    },
83
    setup(props) {
86
    setup(props) {
84
        const verificationChecks = ref([]);
87
        const verificationChecks = ref([]);
85
        const initialized = ref(false);
88
        const initialized = ref(false);
Lines 92-98 export default { Link Here
92
95
93
        onBeforeMount(() => {
96
        onBeforeMount(() => {
94
            const client = APIClient.patron;
97
            const client = APIClient.patron;
95
            client.self_renewal.start().then(
98
            client.self_renewal.start(props.patron).then(
96
                response => {
99
                response => {
97
                    verificationChecks.value = response.verification_checks;
100
                    verificationChecks.value = response.verification_checks;
98
                    renewalSettings.value = {
101
                    renewalSettings.value = {
Lines 126-132 export default { Link Here
126
129
127
        const submitRenewal = () => {
130
        const submitRenewal = () => {
128
            const client = APIClient.patron;
131
            const client = APIClient.patron;
129
            client.self_renewal.submit({}).then(
132
            client.self_renewal.submit(props.patron, {}).then(
130
                response => {
133
                response => {
131
                    let newLocation =
134
                    let newLocation =
132
                        "/cgi-bin/koha/opac-user.pl?self_renewal_success=" +
135
                        "/cgi-bin/koha/opac-user.pl?self_renewal_success=" +
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-memberentry.tt (-2 / +6 lines)
Lines 244-250 Link Here
244
                        >
244
                        >
245
                    [% END %]
245
                    [% END %]
246
246
247
                    <form method="post" [% IF (self_renewal) %]action="/api/v1/public/patrons/self_renewal" id="self-renewal-form"[% ELSE %]action="/cgi-bin/koha/opac-memberentry.pl" id="memberentry-form"[% END %] autocomplete="off">
247
                    <form
248
                        method="post"
249
                        [% IF (self_renewal) %]action="/api/v1/public/patrons/[% logged_in_user.borrowernumber | uri %]/self_renewal" id="self-renewal-form"[% ELSE %]action="/cgi-bin/koha/opac-memberentry.pl" id="memberentry-form"[% END %]
250
                        autocomplete="off"
251
                    >
248
                        [% INCLUDE 'csrf-token.inc' %]
252
                        [% INCLUDE 'csrf-token.inc' %]
249
253
250
                        [% FOREACH field = ['streetnumber' 'streettype'  'cardnumber' 'branchcode' 'categorycode' 'title' 'surname' 'firstname' 'preferred_name' 'middle_name' 'dateofbirth' 'initials' 'pronouns' 'othernames' 'address' 'address2' 'city' 'state' 'zipcode' 'country' 'phone' 'phonepro' 'mobile' 'email' 'emailpro' 'fax' 'B_streettype' 'B_streetnumber' 'B_address' 'B_address2' 'B_city' 'B_state' 'B_zipcode' 'B_country' 'B_phone' 'B_email' 'contactnote' 'altcontactsurname' 'altcontactfirstname' 'altcontactaddress1' 'altcontactaddress2' 'altcontactaddress3' 'altcontactstate' 'altcontactzipcode' 'altcontactcountry' 'altcontactphone' 'password' 'lang' ] %]
254
                        [% FOREACH field = ['streetnumber' 'streettype'  'cardnumber' 'branchcode' 'categorycode' 'title' 'surname' 'firstname' 'preferred_name' 'middle_name' 'dateofbirth' 'initials' 'pronouns' 'othernames' 'address' 'address2' 'city' 'state' 'zipcode' 'country' 'phone' 'phonepro' 'mobile' 'email' 'emailpro' 'fax' 'B_streettype' 'B_streetnumber' 'B_address' 'B_address2' 'B_city' 'B_state' 'B_zipcode' 'B_country' 'B_phone' 'B_email' 'contactnote' 'altcontactsurname' 'altcontactfirstname' 'altcontactaddress1' 'altcontactaddress2' 'altcontactaddress3' 'altcontactstate' 'altcontactzipcode' 'altcontactcountry' 'altcontactphone' 'password' 'lang' ] %]
Lines 1461-1467 Link Here
1461
                                    url: $(form).attr('action'),
1465
                                    url: $(form).attr('action'),
1462
                                    method: $(form).attr('method').toUpperCase(),
1466
                                    method: $(form).attr('method').toUpperCase(),
1463
                                    contentType: 'application/json',
1467
                                    contentType: 'application/json',
1464
                                    data: JSON.stringify(renewalBody)
1468
                                    data: JSON.stringify({patron: renewalBody})
1465
                                };
1469
                                };
1466
                                $.ajax(options)
1470
                                $.ajax(options)
1467
                                    .then(function(result) {
1471
                                    .then(function(result) {
(-)a/koha-tmpl/opac-tmpl/bootstrap/en/modules/opac-user.tt (-1 / +1 lines)
Lines 1035-1041 Link Here
1035
<!-- /#main -->
1035
<!-- /#main -->
1036
1036
1037
[% IF ( self_renewal_available ) %]
1037
[% IF ( self_renewal_available ) %]
1038
    <patron-self-renewal></patron-self-renewal>
1038
    <patron-self-renewal patron="[% borrowernumber | html %]"></patron-self-renewal>
1039
[% END %]
1039
[% END %]
1040
1040
1041
[% SET islands = Asset.js("js/vue/dist/islands.esm.js").match('(src="([^"]+)")').1 %]
1041
[% SET islands = Asset.js("js/vue/dist/islands.esm.js").match('(src="([^"]+)")').1 %]
(-)a/t/cypress/integration/Islands/SelfRenewal.ts (-7 / +10 lines)
Lines 56-62 describe("Patron self-renewal", () => { Link Here
56
        cy.intercept(
56
        cy.intercept(
57
            "GET",
57
            "GET",
58
            Cypress.env("opacBaseUrl") +
58
            Cypress.env("opacBaseUrl") +
59
                "/api/v1/public/patrons/self_renewal?_per_page=-1",
59
                "/api/v1/public/patrons/*/self_renewal?_per_page=-1",
60
            {
60
            {
61
                self_renewal_settings: {
61
                self_renewal_settings: {
62
                    opac_patron_details: "0",
62
                    opac_patron_details: "0",
Lines 78-84 describe("Patron self-renewal", () => { Link Here
78
        cy.intercept(
78
        cy.intercept(
79
            "GET",
79
            "GET",
80
            Cypress.env("opacBaseUrl") +
80
            Cypress.env("opacBaseUrl") +
81
                "/api/v1/public/patrons/self_renewal?_per_page=-1",
81
                "/api/v1/public/patrons/*/self_renewal?_per_page=-1",
82
            {
82
            {
83
                self_renewal_settings: {
83
                self_renewal_settings: {
84
                    opac_patron_details: "0",
84
                    opac_patron_details: "0",
Lines 93-99 describe("Patron self-renewal", () => { Link Here
93
        cy.wait("@renewalConfig");
93
        cy.wait("@renewalConfig");
94
        cy.intercept(
94
        cy.intercept(
95
            "POST",
95
            "POST",
96
            Cypress.env("opacBaseUrl") + "/api/v1/public/patrons/self_renewal",
96
            Cypress.env("opacBaseUrl") +
97
                "/api/v1/public/patrons/*/self_renewal",
97
            {
98
            {
98
                statusCode: 201,
99
                statusCode: 201,
99
                body: {
100
                body: {
Lines 115-121 describe("Patron self-renewal", () => { Link Here
115
        cy.intercept(
116
        cy.intercept(
116
            "GET",
117
            "GET",
117
            Cypress.env("opacBaseUrl") +
118
            Cypress.env("opacBaseUrl") +
118
                "/api/v1/public/patrons/self_renewal?_per_page=-1",
119
                "/api/v1/public/patrons/*/self_renewal?_per_page=-1",
119
            {
120
            {
120
                self_renewal_settings: {
121
                self_renewal_settings: {
121
                    opac_patron_details: "0",
122
                    opac_patron_details: "0",
Lines 133-139 describe("Patron self-renewal", () => { Link Here
133
        cy.wait("@renewalConfig");
134
        cy.wait("@renewalConfig");
134
        cy.intercept(
135
        cy.intercept(
135
            "POST",
136
            "POST",
136
            Cypress.env("opacBaseUrl") + "/api/v1/public/patrons/self_renewal",
137
            Cypress.env("opacBaseUrl") +
138
                "/api/v1/public/patrons/*/self_renewal",
137
            {
139
            {
138
                statusCode: 201,
140
                statusCode: 201,
139
                body: {
141
                body: {
Lines 170-176 describe("Patron self-renewal", () => { Link Here
170
        cy.intercept(
172
        cy.intercept(
171
            "GET",
173
            "GET",
172
            Cypress.env("opacBaseUrl") +
174
            Cypress.env("opacBaseUrl") +
173
                "/api/v1/public/patrons/self_renewal?_per_page=-1",
175
                "/api/v1/public/patrons/*/self_renewal?_per_page=-1",
174
            {
176
            {
175
                self_renewal_settings: {
177
                self_renewal_settings: {
176
                    opac_patron_details: "1",
178
                    opac_patron_details: "1",
Lines 195-201 describe("Patron self-renewal", () => { Link Here
195
        );
197
        );
196
        cy.intercept(
198
        cy.intercept(
197
            "POST",
199
            "POST",
198
            Cypress.env("opacBaseUrl") + "/api/v1/public/patrons/self_renewal",
200
            Cypress.env("opacBaseUrl") +
201
                "/api/v1/public/patrons/*/self_renewal",
199
            {
202
            {
200
                statusCode: 201,
203
                statusCode: 201,
201
                body: {
204
                body: {
(-)a/t/db_dependent/api/v1/patrons_self_renewal.t (-11 / +17 lines)
Lines 63-76 subtest 'start()' => sub { Link Here
63
    $patron->set_password( { password => $password, skip_validation => 1 } );
63
    $patron->set_password( { password => $password, skip_validation => 1 } );
64
    my $userid = $patron->userid;
64
    my $userid = $patron->userid;
65
65
66
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/self_renewal")->status_is( 403, 'REST3.2.2' )
66
    my $borrowernumber = $patron->borrowernumber;
67
        ->json_is( { error => "You are not eligible for self-renewal" } );
67
68
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal")
69
        ->status_is( 403, 'REST3.2.2' )->json_is( { error => "You are not eligible for self-renewal" } );
68
70
69
    $category->self_renewal_enabled(1)->store();
71
    $category->self_renewal_enabled(1)->store();
70
72
71
    t::lib::Mocks::mock_preference( 'OPACPatronDetails', 1 );
73
    t::lib::Mocks::mock_preference( 'OPACPatronDetails', 1 );
72
74
73
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/self_renewal")->status_is( 200, 'REST3.2.2' )->json_is(
75
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal")
76
        ->status_is( 200, 'REST3.2.2' )->json_is(
74
        {
77
        {
75
            verification_checks   => [],
78
            verification_checks   => [],
76
            self_renewal_settings => {
79
            self_renewal_settings => {
Lines 78-84 subtest 'start()' => sub { Link Here
78
                opac_patron_details          => 1
81
                opac_patron_details          => 1
79
            }
82
            }
80
        }
83
        }
81
    );
84
        );
82
85
83
    my $attribute_type = $builder->build_object(
86
    my $attribute_type = $builder->build_object(
84
        {
87
        {
Lines 111-117 subtest 'start()' => sub { Link Here
111
            }
114
            }
112
        }
115
        }
113
    );
116
    );
114
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/self_renewal")->status_is( 200, 'REST3.2.2' )->json_is(
117
    $t->get_ok("//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal")
118
        ->status_is( 200, 'REST3.2.2' )->json_is(
115
        {
119
        {
116
            verification_checks   => [ $attribute_type->unblessed ],
120
            verification_checks   => [ $attribute_type->unblessed ],
117
            self_renewal_settings => {
121
            self_renewal_settings => {
Lines 119-125 subtest 'start()' => sub { Link Here
119
                opac_patron_details          => 1
123
                opac_patron_details          => 1
120
            }
124
            }
121
        }
125
        }
122
    );
126
        );
123
127
124
    $schema->storage->txn_rollback;
128
    $schema->storage->txn_rollback;
125
};
129
};
Lines 155-161 subtest 'submit()' => sub { Link Here
155
    $patron->set_password( { password => $password, skip_validation => 1 } );
159
    $patron->set_password( { password => $password, skip_validation => 1 } );
156
    my $userid = $patron->userid;
160
    my $userid = $patron->userid;
157
161
158
    $t->post_ok( "//$userid:$password@/api/v1/public/patrons/self_renewal" => json => {} )
162
    my $borrowernumber = $patron->borrowernumber;
163
164
    $t->post_ok( "//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal" => json => {} )
159
        ->status_is( 403, 'REST3.2.2' )->json_is( { error => "You are not eligible for self-renewal" } );
165
        ->status_is( 403, 'REST3.2.2' )->json_is( { error => "You are not eligible for self-renewal" } );
160
166
161
    t::lib::Mocks::mock_preference( 'OPACPatronDetails',                1 );
167
    t::lib::Mocks::mock_preference( 'OPACPatronDetails',                1 );
Lines 190-196 subtest 'submit()' => sub { Link Here
190
    );
196
    );
191
197
192
    my $counter = Koha::Notice::Messages->search( { borrowernumber => $patron->borrowernumber } )->count;
198
    my $counter = Koha::Notice::Messages->search( { borrowernumber => $patron->borrowernumber } )->count;
193
    $t->post_ok( "//$userid:$password@/api/v1/public/patrons/self_renewal" => json => {} )
199
    $t->post_ok( "//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal" => json => {} )
194
        ->status_is( 201, 'REST3.2.2' )
200
        ->status_is( 201, 'REST3.2.2' )
195
        ->json_is( { expiry_date => $expiry_date->truncate( to => 'day' ), confirmation_sent => 1 } );
201
        ->json_is( { expiry_date => $expiry_date->truncate( to => 'day' ), confirmation_sent => 1 } );
196
    is(
202
    is(
Lines 200-209 subtest 'submit()' => sub { Link Here
200
206
201
    # Test that modifications are created correctly
207
    # Test that modifications are created correctly
202
    t::lib::Mocks::mock_preference( 'OPACPatronDetails', 1 );
208
    t::lib::Mocks::mock_preference( 'OPACPatronDetails', 1 );
203
    my $modification_data = { firstname => 'Newname' };
209
    my $modification_data = { patron => { firstname => 'Newname' } };
204
    $patron->dateexpiry( dt_from_string() )->store();
210
    $patron->dateexpiry( dt_from_string() )->store();
205
211
206
    $t->post_ok( "//$userid:$password@/api/v1/public/patrons/self_renewal" => json => $modification_data )
212
    $t->post_ok(
213
        "//$userid:$password@/api/v1/public/patrons/$borrowernumber/self_renewal" => json => $modification_data )
207
        ->status_is( 201, 'REST3.2.2' )
214
        ->status_is( 201, 'REST3.2.2' )
208
        ->json_is( { expiry_date => $expiry_date->truncate( to => 'day' ), confirmation_sent => 1 } );
215
        ->json_is( { expiry_date => $expiry_date->truncate( to => 'day' ), confirmation_sent => 1 } );
209
216
210
- 

Return to bug 26355