Bug 34632: Use PatronImport background job in import_borrowers
import_borrowers.pl now reads the uploaded file into memory and calls Koha::BackgroundJob::PatronImport->enqueue() instead of running the import synchronously. On success the template shows a confirmation with a link to the background job viewer; on failure it shows an error alert. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Brendan Lawlor <blawlor@clamsnet.org> Signed-off-by: Lisette Scheer <lisette@bywatersolutions.com> Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This commit is contained in:
parent
c8c24445af
commit
88644d26af
|
|
@ -7,8 +7,8 @@
|
|||
[% INCLUDE 'doc-head-open.inc' %]
|
||||
<title
|
||||
>[% FILTER collapse %]
|
||||
[% IF ( uploadborrowers ) %]
|
||||
[% t("Results") | html %]
|
||||
[% IF op == 'enqueued' %]
|
||||
[% t("Job enqueued") | html %]
|
||||
›
|
||||
[% END %]
|
||||
[% t("Import patrons") | html %]
|
||||
|
|
@ -39,12 +39,12 @@
|
|||
[% WRAPPER breadcrumb_item %]
|
||||
<a href="/cgi-bin/koha/tools/tools-home.pl">Tools</a>
|
||||
[% END %]
|
||||
[% IF ( uploadborrowers ) %]
|
||||
[% IF op == 'enqueued' %]
|
||||
[% WRAPPER breadcrumb_item %]
|
||||
<a href="/cgi-bin/koha/tools/import_borrowers.pl">Import patrons</a>
|
||||
[% END %]
|
||||
[% WRAPPER breadcrumb_item bc_active= 1 %]
|
||||
<span>Results</span>
|
||||
<span>Job enqueued</span>
|
||||
[% END %]
|
||||
[% ELSE %]
|
||||
[% WRAPPER breadcrumb_item bc_active= 1 %]
|
||||
|
|
@ -57,151 +57,20 @@
|
|||
[% WRAPPER 'main-container.inc' aside='tools-menu' %]
|
||||
<h1>Import patrons</h1>
|
||||
<div class="row">
|
||||
[% IF ( uploadborrowers ) %]
|
||||
[% IF op == 'enqueued' %]
|
||||
<div class="col-sm-6">
|
||||
<h2>Import results</h2>
|
||||
<ul>
|
||||
<li>
|
||||
[% tpx("Patron import", "{count_of_imported_records} imported records", {count_of_imported_records = imported}) | html %]
|
||||
[% IF ( lastimported ) %]
|
||||
[% tpx("Patron import", "(last was {last_imported_record})", {last_imported_record = lastimported}) | html %]
|
||||
[% END %]
|
||||
</li>
|
||||
[% IF imported and patronlistname %]
|
||||
<li>Patron list with imported patrons: [% patronlistname | html %]</li>
|
||||
[% END %]
|
||||
<li>
|
||||
[% tpx("Patron import", "{count_of_overwritten_records} overwritten records", {count_of_overwritten_records = overwritten}) | html %]
|
||||
[% IF ( lastoverwritten ) %]
|
||||
[% tpx("Patron import", "(last was {last_overwritten_record})", {last_overwritten_record = lastoverwritten}) | html %]
|
||||
[% END %]
|
||||
</li>
|
||||
<li>
|
||||
[% tpx("Patron import", "{count_of_existing_records} not imported because they are already in borrowers table and overwrite is disabled ", {count_of_existing_records = alreadyindb}) | html %]
|
||||
[% IF ( lastalreadyindb ) %]
|
||||
[% tpx("Patron import", "(last was {last_existing_record})", {last_existing_record = lastalreadyindb}) | html %]
|
||||
[% END %]
|
||||
</li>
|
||||
<li>
|
||||
[% tpx("Patron import", "{count_of_invalid_records} not imported because they are not in the expected format", {count_of_invalid_records = invalid}) | html %]
|
||||
[% IF ( lastinvalid ) %]
|
||||
[% tpx("Patron import", "(last was {last_invalid_record})", {last_invalid_record = lastinvalid}) | html %]
|
||||
[% END %] </li
|
||||
><li> [% tpx("Patron import", "{total} records parsed", {total = total}) | html %] </li>
|
||||
<li><a href="/cgi-bin/koha/tools/tools-home.pl">Back to Tools</a></li>
|
||||
</ul>
|
||||
|
||||
[% IF ( feedback ) %]
|
||||
<br /><br />
|
||||
|
||||
<div>
|
||||
<h2>Feedback</h2>
|
||||
<ul class="feedback">
|
||||
[% FOREACH f IN feedback %]
|
||||
<li>
|
||||
[% IF ( f.filename ) %]
|
||||
<span>Parsing upload file</span> <span class="filename">[% f.filename | html %]</span>
|
||||
[% ELSIF ( f.backend ) %]
|
||||
<span>Upload parsed using [% f.backend | html %]</span>
|
||||
[% ELSIF ( f.headerrow ) %]
|
||||
<span>These fields found: [% f.value | html %]</span>
|
||||
[% ELSIF ( f.already_in_db ) %]
|
||||
<span>Patron already in database: [% f.value | html %]</span>
|
||||
[% ELSE %]
|
||||
[% f.name | html %]
|
||||
: [% f.value | html %]
|
||||
[% END %]
|
||||
</li>
|
||||
[% END %]
|
||||
</ul>
|
||||
</div>
|
||||
[% END %]
|
||||
|
||||
[% IF ( errors ) %]
|
||||
<br /><br />
|
||||
|
||||
<div>
|
||||
<h2>Error analysis</h2>
|
||||
<ul>
|
||||
[% FOREACH e IN errors %]
|
||||
[% IF ( e.badheader ) %]<li>Header row could not be parsed</li>[% END %]
|
||||
|
||||
[% FOREACH missing_critical IN e.missing_criticals %]
|
||||
<li class="line_error">
|
||||
Line <span class="linenumber">[% missing_critical.line | html %]</span>
|
||||
|
||||
[% IF ( missing_critical.badparse ) %]
|
||||
<span>could not be parsed!</span>
|
||||
[% ELSIF ( missing_critical.bad_date ) %]
|
||||
<span>has "[% missing_critical.key | html %]" in unrecognized format:</span> "[% missing_critical.value | html %]"
|
||||
[% ELSE %]
|
||||
<span>Critical field "[% missing_critical.key | html %]"</span>
|
||||
|
||||
[% IF ( missing_critical.branch_map ) %]
|
||||
<span>has unrecognized value "[% missing_critical.value | html %]"</span>
|
||||
[% ELSIF ( missing_critical.category_map ) %]
|
||||
<span>has unrecognized value "[% missing_critical.value | html %]"</span>
|
||||
[% ELSE %]
|
||||
<span>missing</span>
|
||||
[% END %]
|
||||
(<span>borrowernumber: [% missing_critical.borrowernumber | html %]</span>; <span>surname: [% missing_critical.surname | html %]</span>).
|
||||
[% END %]
|
||||
|
||||
<br />
|
||||
<code>[% missing_critical.lineraw | html %]</code>
|
||||
</li>
|
||||
[% END %]
|
||||
|
||||
[% IF e.invalid_cardnumber %]
|
||||
<li class="line_error"> Card number [% e.cardnumber | html %] is not a valid card number [% IF e.borrowernumber %](for patron with borrowernumber [% e.borrowernumber | html %])[% END %] </li>
|
||||
[% END %]
|
||||
[% IF e.duplicate_userid %]
|
||||
<li class="line_error"> Userid [% e.userid | html %] is already used by another patron. </li>
|
||||
[% END %]
|
||||
[% IF e.passwd_too_short %]
|
||||
<li class="line_error"> Password is too short for patron with borrowernumber [% e.borrowernumber | html %]. Minimum length is [% e.min_length | html %], length is [% e.length | html %] </li>
|
||||
[% END %]
|
||||
[% IF e.passwd_whitespace %]
|
||||
<li class="line_error"> Password contains whitespace for patron with borrowernumber [% e.borrowernumber | html %]. </li>
|
||||
[% END %]
|
||||
[% IF e.passwd_too_weak %]
|
||||
<li class="line_error"> Password is too weak for patron with borrowernumber [% e.borrowernumber | html %]. </li>
|
||||
[% END %]
|
||||
[% IF e.passwd_plugin_err %]
|
||||
<li class="line_error"> Password plugin error for patron with borrowernumber [% e.borrowernumber | html %]. </li>
|
||||
[% END %]
|
||||
[% IF e.passwd_unknown_err %]
|
||||
<li class="line_error"> Password error for patron with borrowernumber [% e.borrowernumber | html %]. </li>
|
||||
[% END %]
|
||||
[% IF e.patron_attribute_unique_id_constraint %]
|
||||
<li class="line_error">
|
||||
[% IF e.borrowernumber %]
|
||||
<span>Patron attribute [% e.attribute.code | html %] must be unique for patron with borrowernumber [% e.borrowernumber | html %].</span>
|
||||
[% ELSE %]
|
||||
<span>Patron attribute [% e.attribute.code | html %] must be unique for patron [% e.patron_id | html %].</span>
|
||||
[% END %]
|
||||
</li>
|
||||
[% END %]
|
||||
[% IF e.patron_attribute_invalid_type %]
|
||||
<li class="line_error">
|
||||
[% IF e.borrowernumber %]
|
||||
<span>Patron attribute [% e.attribute_type_code | html %] is invalid for patron with borrowernumber [% e.borrowernumber | html %].</span>
|
||||
[% ELSE %]
|
||||
<span>Patron attribute [% e.attribute_type_code | html %] is invalid for patron [% e.patron_id | html %].</span>
|
||||
[% END %]
|
||||
</li>
|
||||
[% END %]
|
||||
[% IF e.patron_attribute_non_repeatable %]
|
||||
<li class="line_error">
|
||||
[% IF e.borrowernumber %]
|
||||
<span>Patron attribute [% e.attribute.code | html %] is non-repeatable for patron with borrowernumber [% e.borrowernumber | html %].</span>
|
||||
[% ELSE %]
|
||||
<span>Patron attribute [% e.attribute.code | html %] is non-repeatable for patron [% e.patron_id | html %].</span>
|
||||
[% END %]
|
||||
</li>
|
||||
[% END %]
|
||||
[% END %]
|
||||
</ul>
|
||||
[% IF error == 'cannot_enqueue_job' %]
|
||||
<div class="alert alert-danger">The import job could not be enqueued. Please try again.</div>
|
||||
[% ELSE %]
|
||||
<div class="alert alert-info">
|
||||
<h1>The patron import job has been enqueued!</h1>
|
||||
<p>
|
||||
<a href="/cgi-bin/koha/admin/background_jobs.pl?op=view&id=[% job_id | uri %]">View the import results</a>
|
||||
|
|
||||
<a href="/cgi-bin/koha/tools/import_borrowers.pl">Import more patrons</a>
|
||||
|
|
||||
<a href="/cgi-bin/koha/tools/tools-home.pl">Back to Tools</a>
|
||||
</p>
|
||||
</div>
|
||||
[% END %]
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -36,19 +36,16 @@
|
|||
|
||||
use Modern::Perl;
|
||||
|
||||
use Try::Tiny qw( catch try );
|
||||
|
||||
use C4::Auth qw( get_template_and_user );
|
||||
use C4::Output qw( output_and_exit output_html_with_http_headers );
|
||||
use Koha::BackgroundJob::PatronImport;
|
||||
use Koha::Database::Columns;
|
||||
use Koha::Patrons;
|
||||
use Koha::DateUtils qw( dt_from_string );
|
||||
use Koha::Token;
|
||||
use Koha::Libraries;
|
||||
use Koha::Patron::Categories;
|
||||
use Koha::Patron::Attribute::Types;
|
||||
use Koha::List::Patron qw( AddPatronList AddPatronsToList );
|
||||
|
||||
use Koha::Patrons::Import;
|
||||
my $Import = Koha::Patrons::Import->new();
|
||||
use Koha::Patron::Categories;
|
||||
use Koha::Patrons;
|
||||
|
||||
use Text::CSV;
|
||||
|
||||
|
|
@ -105,58 +102,48 @@ my $timestamp = $dt->ymd('-') . ' ' . $dt->hms(':');
|
|||
|
||||
if ( $op eq 'cud-import' && $uploadborrowers && length($uploadborrowers) > 0 ) {
|
||||
|
||||
my $patronlistname = $uploadborrowers . ' (' . $timestamp . ')';
|
||||
my $handle = $input->upload('uploadborrowers');
|
||||
my %defaults = $input->Vars;
|
||||
my $handle = $input->upload('uploadborrowers');
|
||||
my $file_content = do { local $/; <$handle> };
|
||||
my $patronlistname = $uploadborrowers . ' (' . $timestamp . ')';
|
||||
|
||||
my %defaults;
|
||||
for my $k (@columnkeys) {
|
||||
my $v = scalar $input->param($k);
|
||||
$defaults{$k} = $v if defined $v && length $v;
|
||||
}
|
||||
|
||||
my $overwrite_passwords = defined $input->param('overwrite_passwords') ? 1 : 0;
|
||||
my $update_dateexpiry = $input->param('update_dateexpiry') // "";
|
||||
my $return = $Import->import_patrons(
|
||||
{
|
||||
file => $handle,
|
||||
defaults => \%defaults,
|
||||
matchpoint => $matchpoint,
|
||||
overwrite_cardnumber => scalar $input->param('overwrite_cardnumber'),
|
||||
overwrite_passwords => $overwrite_passwords,
|
||||
preserve_extended_attributes => scalar $input->param('ext_preserve') || 0,
|
||||
preserve_fields => \@preserve_fields,
|
||||
update_dateexpiry => $update_dateexpiry ? 1 : 0,
|
||||
update_dateexpiry_from_today => $update_dateexpiry eq "now" ? 1 : 0,
|
||||
update_dateexpiry_from_existing => $update_dateexpiry eq "dateexpiry" ? 1 : 0,
|
||||
send_welcome => $welcome_new,
|
||||
}
|
||||
);
|
||||
|
||||
my $feedback = $return->{feedback};
|
||||
my $errors = $return->{errors};
|
||||
my $imported = $return->{imported};
|
||||
my $overwritten = $return->{overwritten};
|
||||
my $alreadyindb = $return->{already_in_db};
|
||||
my $invalid = $return->{invalid};
|
||||
my $imported_borrowers = $return->{imported_borrowers};
|
||||
try {
|
||||
my $job_id = Koha::BackgroundJob::PatronImport->new->enqueue(
|
||||
{
|
||||
file_content => $file_content,
|
||||
defaults => \%defaults,
|
||||
matchpoint => $matchpoint,
|
||||
overwrite_cardnumber => scalar $input->param('overwrite_cardnumber'),
|
||||
overwrite_passwords => $overwrite_passwords,
|
||||
preserve_extended_attributes => scalar $input->param('ext_preserve') || 0,
|
||||
preserve_fields => \@preserve_fields,
|
||||
update_dateexpiry => $update_dateexpiry ? 1 : 0,
|
||||
update_dateexpiry_from_today => $update_dateexpiry eq "now" ? 1 : 0,
|
||||
update_dateexpiry_from_existing => $update_dateexpiry eq "dateexpiry" ? 1 : 0,
|
||||
send_welcome => $welcome_new,
|
||||
createpatronlist => $createpatronlist,
|
||||
patronlistname => $patronlistname,
|
||||
}
|
||||
);
|
||||
|
||||
if ( $imported && $createpatronlist ) {
|
||||
my $patronlist = AddPatronList( { name => $patronlistname } );
|
||||
AddPatronsToList( { list => $patronlist, borrowernumbers => $imported_borrowers } );
|
||||
$template->param( 'patronlistname' => $patronlistname );
|
||||
}
|
||||
|
||||
my $uploadinfo = $input->uploadInfo($uploadborrowers);
|
||||
foreach ( keys %$uploadinfo ) {
|
||||
push @$feedback, { feedback => 1, name => $_, value => $uploadinfo->{$_}, $_ => $uploadinfo->{$_} };
|
||||
}
|
||||
|
||||
push @$feedback, { feedback => 1, name => 'filename', value => $uploadborrowers, filename => $uploadborrowers };
|
||||
|
||||
$template->param(
|
||||
uploadborrowers => 1,
|
||||
errors => $errors,
|
||||
feedback => $feedback,
|
||||
imported => $imported,
|
||||
overwritten => $overwritten,
|
||||
alreadyindb => $alreadyindb,
|
||||
invalid => $invalid,
|
||||
total => $imported + $alreadyindb + $invalid + $overwritten,
|
||||
);
|
||||
$template->param(
|
||||
op => 'enqueued',
|
||||
job_id => $job_id,
|
||||
);
|
||||
} catch {
|
||||
$template->param(
|
||||
op => 'enqueued',
|
||||
error => 'cannot_enqueue_job',
|
||||
);
|
||||
};
|
||||
|
||||
} else {
|
||||
if ($extended) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue