Bug 24005

Summary: Software error: at login
Product: Koha Reporter: Andrew Lau <andrewanandal>
Component: Installation and upgrade (command-line installer)Assignee: Bugs List <koha-bugs>
Status: RESOLVED DUPLICATE QA Contact: Testopia <testopia>
Severity: normal    
Priority: P2 CC: jonathan.druart
Version: 19.05   
Hardware: PC   
OS: Linux   
Change sponsored?: --- Patch complexity: ---
Documentation contact: Documentation submission:
Text to go in the release notes:
Version(s) released in:
Attachments: Error Page

Description Andrew Lau 2019-11-09 04:50:32 UTC
Created attachment 95245 [details]
Error Page

Dear all

Greetings! I am a new Koha user establishing library system for a non-profit postgraduate educational institution. Nice to meet all developers and thank you for making Koha freely accessible to all globally. 


Just now I have installed Koha 19.05 on Ubuntu using Ubuntu 18.04.3 LTS terminal, following all the steps in the webinstaller. I attempted logging in using the newly created administrator password. An error message popped up in the subsequent screen:


______________________________________________________________________

Software error:

DBIx::Class::Storage::DBI::_dbh_execute(): Table 'koha_library.discharges' doesn't exist at /usr/share/koha/lib/Koha/Patron/Discharge.pm line 31

For help, please send mail to the webmaster ([no address given]), giving this error message and the time and date of the error. 

______________________________________________________________________


Attached is the error screen for your reference.


For your information, the MySQL on the Ubuntu 18.04.3 LTS downloaded through Ubuntu terminal using "sudo apt-get mysql-server". Also during installation, I followed all the steps as mentioned on https://wiki.koha-community.org/wiki/Koha_on_ubuntu_-_packages. The patron categories and administrator account were already set up during the webinstall stage. 

Could anyone give some pointers ahead? Most grateful for all your professional advice!

Best 
Andrew
Comment 1 Andrew Lau 2019-11-09 10:06:05 UTC
No Executing opening the discharge.pm at the destination folder, the following is obtained:



---------------------------------------------------------------------------
  GNU nano 2.9.3                                                                                                /usr/share/koha/lib/Koha/Patron/Discharge.pm                                                                                                Modified  


    my $cond = {
        'me.needed'    => { '!=', undef },
        'me.validated' => undef,
        ( defined $borrowernumber ? ( 'me.borrower' => $borrowernumber ) : () ),
        ( defined $branchcode ? ( 'borrower.branchcode' => $branchcode ) : () ),
    };

    return search_limited( $cond );
}

sub get_validated {
    my ($params)       = @_;
    my $branchcode     = $params->{branchcode};
    my $borrowernumber = $params->{borrowernumber};

    my $cond = {
        'me.validated' => { '!=', undef },
        ( defined $borrowernumber ? ( 'me.borrower' => $borrowernumber ) : () ),
        ( defined $branchcode ? ( 'borrower.branchcode' => $branchcode ) : () ),
    };

    return search_limited( $cond );
}

# TODO This module should be based on Koha::Object[s]
sub search_limited {
    my ( $params, $attributes ) = @_;
    my $userenv = C4::Context->userenv;
    my @restricted_branchcodes;
    if ( $userenv and $userenv->{number} ) {
        my $logged_in_user = Koha::Patrons->find( $userenv->{number} );
        @restricted_branchcodes = $logged_in_user->libraries_where_can_see_patrons;
    }
    $params->{'borrower.branchcode'} = { -in => \@restricted_branchcodes } if @restricted_branchcodes;
    $attributes->{join} = 'borrower';

    my $rs = Koha::Database->new->schema->resultset('Discharge');
    return $rs->search( $params, { join => 'borrower' } );
}

1;

------------------------------------------------------------------------
Comment 2 Andrew Lau 2019-11-09 10:14:55 UTC
This is the complete text from nano at Discharge.pm


-------------------------------------------------------------------------

  GNU nano 2.9.3                                                                                                                                                                                                  /usr/share/koha/lib/Koha/Patron/Discharge.pm                                                                                                                                                                                                   Modified  

package Koha::Patron::Discharge;

use Modern::Perl;
use CGI;
use File::Temp qw( :POSIX );
use Carp;

use C4::Templates qw ( gettemplate );
use C4::Letters qw ( GetPreparedLetter );

use Koha::Database;
use Koha::DateUtils qw( dt_from_string output_pref );
use Koha::Patrons;
use Koha::Patron::Debarments;

sub count {
    my ($params) = @_;
    my $values = {};

    if( $params->{borrowernumber} ) {
        $values->{borrower} = $params->{borrowernumber};
    }
    if( $params->{pending} ) {
        $values->{needed} = { '!=', undef };
        $values->{validated} = undef;
    }
    elsif( $params->{validated} ) {
        $values->{validated} = { '!=', undef };
    }

    return search_limited( $values )->count;
}

sub can_be_discharged {
    my ($params) = @_;
    return unless $params->{borrowernumber};

    my $patron = Koha::Patrons->find( $params->{borrowernumber} );
    return unless $patron;

    my $has_pending_checkouts = $patron->checkouts->count;
    return $has_pending_checkouts ? 0 : 1;
}

sub is_discharged {
    my ($params) = @_;
    return unless $params->{borrowernumber};
    my $borrowernumber = $params->{borrowernumber};

    my $restricted = Koha::Patrons->find( $borrowernumber )->is_debarred;
    my @validated = get_validated({borrowernumber => $borrowernumber});

    if ($restricted && @validated) {
        return 1;
    } else {
        return 0;
    }
}

sub request {
    my ($params) = @_;
    my $borrowernumber = $params->{borrowernumber};
    return unless $borrowernumber;
    return unless can_be_discharged({ borrowernumber => $borrowernumber });

    my $rs = Koha::Database->new->schema->resultset('Discharge');
    return $rs->create({
        borrower => $borrowernumber,
        needed   => dt_from_string,
    });
}

sub discharge {
    my ($params) = @_;
    my $borrowernumber = $params->{borrowernumber};
    return unless $borrowernumber and can_be_discharged( { borrowernumber => $borrowernumber } );

    # Cancel reserves
    my $patron = Koha::Patrons->find( $borrowernumber );
    my $holds = $patron->holds;
    while ( my $hold = $holds->next ) {
        $hold->cancel;
    }

    # Debar the member
    Koha::Patron::Debarments::AddDebarment({
        borrowernumber => $borrowernumber,
        type           => 'DISCHARGE',
    });

    # Generate the discharge
    my $rs = Koha::Database->new->schema->resultset('Discharge');
    my $discharge = $rs->search({ borrower => $borrowernumber }, { order_by => { -desc => 'needed' }, rows => 1 });
    if( $discharge->count > 0 ) {
        $discharge->update({ validated => dt_from_string });
    }
    else {
        $rs->create({
            borrower  => $borrowernumber,
            validated => dt_from_string,
        });
    }
}

sub generate_as_pdf {
    my ($params) = @_;
    return unless $params->{borrowernumber};

    my $patron = Koha::Patrons->find( $params->{borrowernumber} );
    my $letter = C4::Letters::GetPreparedLetter(
        module      => 'members',
        letter_code => 'DISCHARGE',
        lang        => $patron->lang,
        tables      => { borrowers => $params->{borrowernumber}, branches => $params->{'branchcode'}, },
    );

    my $today = output_pref( dt_from_string() );
    $letter->{'title'}   =~ s/<<today>>/$today/g;
    $letter->{'content'} =~ s/<<today>>/$today/g;

    my $tmpl = C4::Templates::gettemplate('batch/print-notices.tt', 'intranet', new CGI);
    $tmpl->param(
        stylesheet => C4::Context->preference("NoticeCSS"),
        today      => $today,
        messages   => [$letter],
    );

    my $html_path = tmpnam() . '.html';
    my $pdf_path = tmpnam() . '.pdf';
    my $html_content = $tmpl->output;
    open my $html_fh, '>:encoding(utf8)', $html_path;
    say $html_fh $html_content;
    close $html_fh;
    my $output = eval { require PDF::FromHTML; return; } || $@;
    if ($output && $params->{testing}) {
        carp $output;
        $pdf_path = undef;
    }
    elsif ($output) {
        die $output;
    }
    else {
        my $pdf = PDF::FromHTML->new( encoding => 'utf-8' );
        $pdf->load_file( $html_path );
        $pdf->convert;
        $pdf->write_file( $pdf_path );
    }

    return $pdf_path;
}

sub get_pendings {
    my ($params)       = @_;
    my $branchcode     = $params->{branchcode};
    my $borrowernumber = $params->{borrowernumber};

    my $cond = {
        'me.needed'    => { '!=', undef },
        'me.validated' => undef,
        ( defined $borrowernumber ? ( 'me.borrower' => $borrowernumber ) : () ),
        ( defined $branchcode ? ( 'borrower.branchcode' => $branchcode ) : () ),
    };

    return search_limited( $cond );
}

sub get_validated {
    my ($params)       = @_;
    my $branchcode     = $params->{branchcode};
    my $borrowernumber = $params->{borrowernumber};

    my $cond = {
        'me.validated' => { '!=', undef },
        ( defined $borrowernumber ? ( 'me.borrower' => $borrowernumber ) : () ),
        ( defined $branchcode ? ( 'borrower.branchcode' => $branchcode ) : () ),
    };

    return search_limited( $cond );
}

# TODO This module should be based on Koha::Object[s]
sub search_limited {
    my ( $params, $attributes ) = @_;
    my $userenv = C4::Context->userenv;
    my @restricted_branchcodes;
    if ( $userenv and $userenv->{number} ) {
        my $logged_in_user = Koha::Patrons->find( $userenv->{number} );
        @restricted_branchcodes = $logged_in_user->libraries_where_can_see_patrons;
    }
    $params->{'borrower.branchcode'} = { -in => \@restricted_branchcodes } if @restricted_branchcodes;
    $attributes->{join} = 'borrower';

    my $rs = Koha::Database->new->schema->resultset('Discharge');
    return $rs->search( $params, { join => 'borrower' } );
}

1;



------------------------------------------------------------------------
Wonder if the table 'koha_library.discharges' should simply be added back at line 31.
Comment 3 Jonathan Druart 2019-11-11 14:01:35 UTC
Hi Andrew, and welcome!

You should not need to modify anything after the installation process.
Do you see the different tables in the database? How many?

I would recommend you to remove the instance of Koha you have created and start again.

Also, it could help to take a look at the logs (/var/log/koha/YOUR_INSTANCE) to see if there is something useful there.
Comment 4 Andrew Lau 2019-11-12 04:19:45 UTC
Many thanks for the very practical advice! I removed the instance and attempted reinstalling it. Now the following error message occurs:




-----------------------------------------
The following error occurred while importing the database structure:

[Tue Nov 12 12:04:18 2019] install.pl: DBD::mysql::st execute failed: BLOB, TEXT, GEOMETRY or JSON column 'changed_fields' can't have a default value at /usr/share/perl5/DBIx/RunSQL.pm line 273.

Please contact your system administrator
------------------------------------------------------------


The /usr/share/perl5/DBIx?RunSQL.pm looks like this:

--------------------------------------------------------------
  GNU nano 2.9.3                                                                         /usr/share/perl5/DBIx/RunSQL.pm                                                                                    

package DBIx::RunSQL;
use strict;
use DBI;

use vars qw($VERSION);
$VERSION = '0.15';

=head1 NAME

DBIx::RunSQL - run SQL from a file

=cut

=head1 SYNOPSIS

    #!/usr/bin/perl -w
    use strict;
    use lib 'lib';
    use DBIx::RunSQL;

    my $test_dbh = DBIx::RunSQL->create(
        dsn     => 'dbi:SQLite:dbname=:memory:',
        sql     => 'sql/create.sql',
        force   => 1,
        verbose => 1,
    );

    ... # run your tests with a DB setup fresh from setup.sql

=head1 METHODS

=head2 C<< DBIx::RunSQL->create ARGS >>

=head2 C<< DBIx::RunSQL->run ARGS >>

Runs the SQL commands and returns the database handle.
In list context, it returns the database handle and the
suggested exit code.

=over 4

=item *

C<sql> - name of the file containing the SQL statements

The default is C<sql/create.sql>

If C<sql> is a reference to a glob or a filehandle,
the SQL will be read from that. B<not implemented>

This allows one to create SQL-as-programs as follows:

  #!/usr/bin/perl -w -MDBIx::RunSQL -e 'create()'
  create table ...

If you want to run SQL statements from a scalar,
you can simply pass in a reference to a scalar containing the SQL:

    sql => \"update mytable set foo='bar';",

=item *

C<dsn>, C<user>, C<password> - DBI parameters for connecting to the DB

=item *

C<dbh> - a premade database handle to be used instead of C<dsn>

=item *

C<force> - continue even if errors are encountered

=item *

C<verbose> - print each SQL statement as it is run

=item *

C<verbose_handler> - callback to call with each SQL statement instead of C<print>

=item *

C<verbose_fh> - filehandle to write to instead of C<STDOUT>

=back

=cut

sub create {
    my ($self,%args) = @_;
    $args{sql} ||= 'sql/create.sql';

    my $dbh = delete $args{ dbh };
    if (! $dbh) {
        $dbh = DBI->connect($args{dsn}, $args{user}, $args{password}, {})
            or die "Couldn't connect to DSN '$args{dsn}' : " . DBI->errstr;
    };

Runs an SQL file on a prepared database handle.
Returns the number of errors encountered.

If the statement returns rows, these are printed
separated with tabs.

=over 4

=item *

C<dbh> - a premade database handle

=item *

C<sql> - name of the file containing the SQL statements

=item *

C<force> - continue even if errors are encountered

=item *

C<verbose> - print each SQL statement as it is run

=item *

C<verbose_handler> - callback to call with each SQL statement instead of C<print>

=item *

C<verbose_fh> - filehandle to write to instead of C<STDOUT>

=item *

C<output_bool> - whether to exit with a nonzero exit code if any row is found

This makes the function return a nonzero value even if there is no error
but a row was found.

=item *

C<output_string> - whether to output the (one) row and column, without any headers

=back

=cut

sub run_sql_file {
    my ($self,%args) = @_;
    my @sql;
   {
        open my $fh, "<", $args{sql}
            or die "Couldn't read '$args{sql}' : $!";
        # potentially this should become C<< $/ = ";\n"; >>
        # and a while loop to handle large SQL files
        local $/;
        $args{ sql }= <$fh>; # sluuurp
    };
    $self->run_sql(
        %args
    );
}

=head2 C<< DBIx::RunSQL->run_sql ARGS >>

    my $dbh = DBI->connect(...)

    for my $file (sort glob '*.sql') {
        DBIx::RunSQL->run_sql_file(
            verbose => 1,
            dbh     => $dbh,
            sql     => 'create table foo',
        );
    };

Runs an SQL string on a prepared database handle.
Returns the number of errors encountered.

If the statement returns rows, these are printed
separated with tabs, but see the C<output_bool> and C<output_string> options.

=over 4

=item *

C<dbh> - a premade database handle

=item *

C<sql> - string or array reference containing the SQL statements

=item *

C<force> - continue even if errors are encountered

=item *

C<verbose> - print each SQL statement as it is run

=item *

C<verbose_handler> - callback to call with each SQL statement instead of C<print>

=item *

C<verbose_fh> - filehandle to write to instead of C<STDOUT>

=item *

C<output_bool> - whether to exit with a nonzero exit code if any row is found

This makes the function return a nonzero value even if there is no error
but a row was found.

=item *

C<output_string> - whether to output the (one) row and column, without any headers

=back

=cut

sub run_sql {
    my ($self,%args) = @_;
    my $errors = 0;

-------------------------------------------------------------------------
The page runs on for another hundred lines.

Would there be any method to overcome this? That seems to be a problem with the compatability with latest MySQL occuring at Bugs 16775.

Thank you again for all the assistance extended!
Comment 5 Jonathan Druart 2019-11-12 07:40:06 UTC
Hello Andrew,

You are hitting an issue that has been fixed by bug 23579, and that will be part of the next 19.05.x version (19.05.05, not released yet).
You should try with MariaDB instead of MySQL.

Also it is not needed to copy the code into the comment, I have access to it as you provided me the version you are using :)

Good luck!
Comment 6 Jonathan Druart 2019-11-19 09:59:48 UTC

*** This bug has been marked as a duplicate of bug 23579 ***