Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

import invoice email #246

Merged
merged 100 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
100 commits
Select commit Hold shift + click to select a range
cdc08b4
IMAPClient: Funktion zum Aufräumen der Belegordner umbenannt
z4m1n0 Sep 12, 2023
048775d
IMAPClient: Funktion zum Aufräumen von importierten Emails hinzugefügt
z4m1n0 Sep 12, 2023
33475bc
BJ: ImportPurchaseInvoiceEmails hinzugefügt
z4m1n0 Sep 12, 2023
edb6e61
ZUGFeRD: Kreditorenbuchung direkt aus ZUGFeRD-XML erstellen
z4m1n0 Sep 13, 2023
687f251
EmailJournal: ZUGFeRD-Emailanhänge in Kreditorenbuchen konvertieren
z4m1n0 Sep 13, 2023
5d052af
ImportPurchaseInvoiceEmails: Email Client und Ordner im Objekt speichern
z4m1n0 Sep 13, 2023
d7a089e
FIX: EmailImport: korrigiere EmailJournal-Key zu email_journals
z4m1n0 Sep 13, 2023
ac951a7
ImportPurchaseInvoiceEmails: Funktion zum Einlesen von ZUGFeRD-Rechnu…
z4m1n0 Sep 13, 2023
70b4058
IMAPClient: Funktion zum setzen von Email-Flags
z4m1n0 Sep 18, 2023
7e62d6a
ImportRecordEmails: BJ zum importieren von Emails als Beleg-Grundlage
z4m1n0 Sep 18, 2023
ca5695e
Presenter: EmailJournal: Funktion zum Anzeigen des Status
z4m1n0 Sep 19, 2023
4e8c7e8
EmailJournal: design40 überarbeitet
z4m1n0 Sep 19, 2023
0266107
HTML::Restrict: Tabellen-Tags zu eingeschränkten HTML-Tags hinzugefügt
z4m1n0 Sep 19, 2023
3e3ec86
Presenter: EmailJournal: Helferfunktion um E-mailanhänge anzuzeigen
z4m1n0 Sep 19, 2023
0611814
EmailJournal: Zeige Vorschau von Anhängen an
z4m1n0 Sep 19, 2023
2064c05
EmailJournalAttachment: Funktion um Datei zum Beleg hinzufügen
z4m1n0 Sep 21, 2023
f642a66
Helper::EmailProcessing: Nutze neue Funktionen
z4m1n0 Sep 21, 2023
3bacc5a
EmailJournal: Zusätzlicher Type für Beleg-Importe
z4m1n0 Sep 21, 2023
42553cb
BJ::ImportRecordEmails: Nutze neuen Typ für Beleg-Importe
z4m1n0 Sep 21, 2023
0245e66
EmailJournal: Basisfunktionalität fürs Verlinken und Neu erstellen vo…
z4m1n0 Sep 21, 2023
92fe2db
Presenter::EmailJournal: Anhang-Vorschaufunktion mit ID oder Objekt
z4m1n0 Sep 22, 2023
c3710cb
S:D:Reclamation: Nutze TypeData zum bestimmen ob VK/EK-Reklamation
z4m1n0 Oct 6, 2023
cdf3277
Workflow: E-Mail → Angebot/Auftrag
z4m1n0 Sep 22, 2023
50a4349
IMAPClient: FIX: Fehler beim Parsen des Datum behoben
z4m1n0 Sep 27, 2023
abc52ab
SL::DB::Helper::ZUGFeRD: FIX: fehlendes use
z4m1n0 Sep 27, 2023
a991586
IMAPClient: FIX: säubere den MIME-Type von Anhängen
z4m1n0 Oct 6, 2023
3933a9e
S:C:Base: erlaube Überschreibung von content_disposition beim Datei s…
z4m1n0 Oct 6, 2023
b40c5ce
EmailJournal: Anhangsvorschau mit kopierbaren Text
z4m1n0 Oct 6, 2023
dfc2529
EmailJournal: FIX: Status im Bericht richtig anzeigen
z4m1n0 Oct 6, 2023
0c20941
TypeData: Füge Übersetzung für Typ hinzu
z4m1n0 Oct 7, 2023
6f5e896
S:D:Order::TypeData: Typenreihenfolge angepasst
z4m1n0 Oct 7, 2023
a7a69ac
EmailJournal: Templates überarbeitet und Verknüpfte Belege hinzugefügt
z4m1n0 Oct 7, 2023
4f5672d
Entferne Debug Statements
z4m1n0 Oct 12, 2023
0196da3
EmailJournalAttachment: FIX: entferne nicht genutzten Import
z4m1n0 Oct 12, 2023
6b8eefa
FIX: Kein Workflow von Storno Rechnung zu Reklamation
z4m1n0 Oct 13, 2023
0cf1c8d
EmailJournal: Workflow angepasst
z4m1n0 Oct 13, 2023
77e25a2
EmailJournal: Funktion für Anhangsvorschau hinzugefügt
z4m1n0 Oct 13, 2023
966e4a1
Workflow: E-Mail → Angebot/Auftrag überarbeitet
z4m1n0 Oct 13, 2023
e0e3780
Workflow: E-Mail → Lieferschein hinzugefügt
z4m1n0 Oct 13, 2023
eedbd12
Workflow: E-Mail → Reklamation hinzugefügt
z4m1n0 Oct 13, 2023
505f6be
Workflow: E-Mail → VK Rechnung/Gutschrift überarbeitet
z4m1n0 Oct 13, 2023
992580f
Workflow: E-Mail → EK Rechnung/Gutschrift hinzugefügt
z4m1n0 Oct 13, 2023
dd14fc3
Workflow: E-Mail → Debitorenbuchung hinzugefügt
z4m1n0 Oct 13, 2023
5e212d3
Workflow: E-Mail → Kreditorenbuchung hinzugefügt
z4m1n0 Oct 13, 2023
53c4227
SL::IMAPClient: Nutze das richtige Encoding für Header und Body
z4m1n0 Oct 17, 2023
cc8086c
EmailJournal: Zeige den Text richtig an.
z4m1n0 Oct 17, 2023
7fa5b6d
FIX: Dokumente in EK-Rechnungen anzeigen
z4m1n0 Oct 17, 2023
c632781
S:C:File: Alle Optionen für Belege eingeschaltet
z4m1n0 Oct 17, 2023
635cb9a
FIX: richtige Query fürs Suchen nach offenen VK-Rechnungen
z4m1n0 Oct 23, 2023
fc264bc
TypeData: nutze richtige Übersetzung für Typ
z4m1n0 Oct 23, 2023
a48aab4
EK/VK-Rechnung Typefilter für alle vorhandenen Typen erweitert
z4m1n0 Oct 24, 2023
e283623
EmailJournal: Workflow: Vorschlag der passenden Belege
z4m1n0 Oct 24, 2023
7cf2da3
EmailJournal: Workflow: Filter Belege nach Belegnummer
z4m1n0 Oct 24, 2023
1c62fca
EmailJournal: Workflow: Auswahl von Beleg optimiert
z4m1n0 Oct 24, 2023
28ea4e7
locales
z4m1n0 Oct 24, 2023
8d42583
EmailJournal: Report: Sortiere nach verknüpften Emails
z4m1n0 Oct 24, 2023
cfe4185
SL::Presenter::BankTransaction: einfacher Presenter für Bankbuchungen
z4m1n0 Oct 25, 2023
5353aec
SL::DB::SepaExportItem: einfacher Presenter für SEPA-Überweisungen
z4m1n0 Oct 25, 2023
60cba3e
S:Presenter:Record: Alias Funktion "show" in verwendeten Objekten
z4m1n0 Oct 25, 2023
148785d
SL::Presenter::Record: Schlichte Darstellung für verknüpfte Belege.
z4m1n0 Oct 25, 2023
4e0d059
EmailJournal: Zeige verknüpfte Belege im Bericht an
z4m1n0 Oct 25, 2023
fd43cab
TypeData: Füge Eigenschaft ob Beleg einen Workflow braucht hinzu
z4m1n0 Nov 1, 2023
8a87257
FIX: typo in changelog
z4m1n0 Nov 1, 2023
6088e24
S:D:RecordTemplate: um Standard-Helferfunktionen erweitert
z4m1n0 Nov 1, 2023
5e9f19c
EmailJournal: Belegvorlage zum Workflow hinzugefügt
z4m1n0 Nov 1, 2023
6dc75be
Belegvorlagen + Dialogbuchung: Workflow von EmailJournal aus umgesetzt
z4m1n0 Nov 3, 2023
26dbf59
ZUGFeRD: Verschiebe komplettes Parsen aus ap.pl nach S:C:ZUGFeRD
z4m1n0 Nov 9, 2023
231ea4d
ZUGFeRD: Anpassung nach Rebase: Kreditorenbuchung direkt aus ZUGFeRD-XML
z4m1n0 Nov 9, 2023
b0d95d7
ImportRecordEmails: Anpassung nach Rebase: BJ zum importieren von Emails
z4m1n0 Nov 9, 2023
5c61290
EmailJournal: DB: um Ungültig-Feld erweitert
z4m1n0 Nov 11, 2023
cada7dd
EmailJournal: DB: Beleg-Type um erweitert
z4m1n0 Nov 11, 2023
ea3ddb0
EmailJournal: Obsolete anzeigen und umschalten können
z4m1n0 Nov 11, 2023
7138b17
EmailJournal: Belegtyp anzeigen
z4m1n0 Nov 11, 2023
b55ff8d
EmailJournal: Report um Obsolete, Belegtype und Verknüpft erweitert
z4m1n0 Nov 11, 2023
bf0cbcd
EmailJournal: altes Design für neuen Workflow und Felder angepasst
z4m1n0 Nov 12, 2023
d776ce2
FIX: S:C:Reclamation: Flasche Referenz auf Controller-Objekt
z4m1n0 Nov 28, 2023
524027f
Typedata für Invoice und PurchaseInvoice
z4m1n0 Nov 13, 2023
6615e40
EmailJournal: suche auch über Email-Domain nach Kunde/Lieferant
z4m1n0 Nov 28, 2023
d3ebfa6
EmailJournal: Wähle Einkauf aus wenn Belegtype Kreditorenbuchung ist
z4m1n0 Nov 28, 2023
d714c18
EmailJournal: Belegtype um Lieferantenauftragsbestätigung erweitert
z4m1n0 Nov 28, 2023
48508b2
Speichern/Buchen und schließen zu Belegen hinzufügt
z4m1n0 Nov 28, 2023
e68a252
EmailJournal: Workflow mit Callback zu Bericht
z4m1n0 Nov 28, 2023
8bb6390
FIX: EmailJournal: Dateitype für Einkaufsgutschrift angepasst
z4m1n0 Nov 28, 2023
7333bc4
FIX: Buchungen: Gebe bei Storno zugehörige Buchungsnummer aus
z4m1n0 Nov 28, 2023
2435a25
Entferne Debug Statements
z4m1n0 Nov 29, 2023
4ac4488
EmailJournal: nutze Presenter::Record um Belege anzuzeigen
z4m1n0 Nov 29, 2023
437737b
S:P:Record: um Belegvorlagen erweitert
z4m1n0 Nov 29, 2023
af42bdf
BJ:ImportRecordEmails: Konfiguration vereinfacht
z4m1n0 Nov 30, 2023
dec0e49
FIX: S:H:EmailProcessing: automatischer ZUGFeRD-Import
z4m1n0 Nov 30, 2023
817e630
EmailJournal: Anhang als ZUGFeRD importieren
z4m1n0 Dec 1, 2023
537f8f5
EmailJournal: Belegtype Catch-All hinzugefügt
z4m1n0 Dec 1, 2023
0292a56
EmailJournal: Belegtype übersetzt anzeigen
z4m1n0 Dec 1, 2023
360dbe6
IMAPClient: FIX: Suche nach Textteil der E-Mail
z4m1n0 Dec 8, 2023
513eeb7
S:B:SyncEmailFolder: FIX: verständliche Fehlermeldung wenn IMAP deakt…
z4m1n0 Dec 8, 2023
9ea083c
IMAPClient: validiere Parameter bei der Übergabe
z4m1n0 Dec 20, 2023
cc96274
Email Background Jobs: validiere Daten-Feld
z4m1n0 Dec 20, 2023
77a5beb
EmailJournal: ZUGFeRD-Import mit ausgewähter Buchungsvorlage
z4m1n0 Dec 20, 2023
812c15a
S:P:Record: Dialogbuchungen nach Keditoren- und Debitorenbuchen anzeigen
z4m1n0 Dec 20, 2023
7dcd422
EmailJournal: Workflow Kreditoren-BV: immer versuchen ZUGFeRD-Daten z…
z4m1n0 Dec 23, 2023
91abb12
S:B:ImportRecordEmails: Nötige Daten beim Validieren einfordern
z4m1n0 Dec 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions SL/AP.pm
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ use SL::DB::Default;
use SL::DB::Draft;
use SL::DB::Order;
use SL::DB::PurchaseInvoice;
use SL::DB::EmailJournal;
use SL::DB::ValidityToken;
use SL::Util qw(trim);
use SL::DB;
Expand Down Expand Up @@ -1106,6 +1107,19 @@ sub _storno {

map { IO->set_datepaid(table => 'ap', id => $_, dbh => $dbh) } ($id, $new_id);

if ($form->{workflow_email_journal_id}) {
my $ap_transaction_storno = SL::DB::PurchaseInvoice->new(id => $new_id)->load;
my $email_journal = SL::DB::EmailJournal->new(
id => delete $form->{workflow_email_journal_id}
)->load;
$email_journal->link_to_record_with_attachment(
$ap_transaction_storno,
delete $form->{workflow_email_attachment_id}
);
$form->{callback} = delete $form->{workflow_email_callback};
}

$form->{storno_id} = $id;
return 1;
}

Expand Down
15 changes: 15 additions & 0 deletions SL/AR.pm
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ use SL::DB::Draft;
use SL::IO;
use SL::MoreCommon;
use SL::DB::Default;
use SL::DB::Invoice;
use SL::DB::EmailJournal;
use SL::DB::ValidityToken;
use SL::TransNumber;
use SL::Util qw(trim);
Expand Down Expand Up @@ -980,6 +982,19 @@ sub _storno {

map { IO->set_datepaid(table => 'ar', id => $_, dbh => $dbh) } ($id, $new_id);

if ($form->{workflow_email_journal_id}) {
my $ar_transaction_storno = SL::DB::Invoice->new(id => $new_id)->load;
my $email_journal = SL::DB::EmailJournal->new(
id => delete $form->{workflow_email_journal_id}
)->load;
$email_journal->link_to_record_with_attachment(
$ar_transaction_storno,
delete $form->{workflow_email_attachment_id}
);
$form->{callback} = delete $form->{workflow_email_callback};
}

$form->{storno_id} = $id;
return 1;
}

Expand Down
8 changes: 4 additions & 4 deletions SL/BackgroundJob/CleanUpEmailSubfolders.pm
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use parent qw(SL::BackgroundJob::Base);

use SL::IMAPClient;

sub clean_up_subfolders {
sub clean_up_record_subfolders {
my ($self) = @_;
my $imap_client = SL::IMAPClient->new();
my $imap_client = SL::IMAPClient->new(%{$::lx_office_conf{imap_client}});

my $open_sales_orders = SL::DB::Manager::Order->get_all(
query => [
Expand All @@ -18,14 +18,14 @@ sub clean_up_subfolders {
],
);

$imap_client->clean_up_subfolders($open_sales_orders);
$imap_client->clean_up_record_subfolders(active_records => $open_sales_orders);
}

sub run {
my ($self, $job_obj) = @_;
$self->{job_obj} = $job_obj;

$self->clean_up_subfolders();
$self->clean_up_record_subfolders();

return;
}
Expand Down
231 changes: 231 additions & 0 deletions SL/BackgroundJob/ImportRecordEmails.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
package SL::BackgroundJob::ImportRecordEmails;

use strict;
use warnings;

use parent qw(SL::BackgroundJob::Base);

use SL::IMAPClient;
use SL::DB::EmailJournal;
use SL::DB::Manager::EmailImport;
use SL::Helper::EmailProcessing;
use SL::Presenter::Tag qw(link_tag);

use Params::Validate qw(:all);
use List::MoreUtils qw(any);

sub sync_record_email_folder {
my ($self, $config) = @_;

my %imap_config;
foreach my $key (qw(enabled hostname port ssl username password base_folder)) {
if (defined $config->{$key}) {
$imap_config{$key} = $config->{$key};
}
}

my $imap_client = SL::IMAPClient->new(%imap_config);

my $email_import = $imap_client->update_emails_from_folder(
folder => $config->{folder},
email_journal_params => {
record_type => $config->{record_type},
}
);
return "No emails to import." unless $email_import;

my $result = "Created email import with id " . $email_import->id . ".";

if ($config->{process_imported_emails}) {
my @function_names =
ref $config->{process_imported_emails} eq 'ARRAY' ?
@{$config->{process_imported_emails}}
: ($config->{process_imported_emails});
foreach my $email_journal (@{$email_import->email_journals}) {
my $created_records = 0;
foreach my $function_name (@function_names) {
eval {
my $processed = SL::Helper::EmailProcessing->process_attachments($function_name, $email_journal);
$created_records += $processed;
1;
} or do {
# # TODO: link not shown as link
# my $email_journal_link = link_tag(
# $ENV{HTTP_ORIGIN} . $ENV{REQUEST_URI}
# . '?action=EmailJournal/show'
# . '&id=' . $email_journal->id
# # text
# , $email_journal->id
# );
my $email_journal_id = $email_journal->id;
$result .= "Error while processing email journal $email_journal_id attachments with $function_name: $@";
};
}
if ($created_records && $config->{processed_imap_flag}) {
$imap_client->set_flag_for_email(
email_journal => $email_journal,
flag => $config->{processed_imap_flag},
);
} elsif ($config->{not_processed_imap_flag}) {
$imap_client->set_flag_for_email(
email_journal => $email_journal,
flag => $config->{not_processed_imap_flag},
);
}
}
$result .= "Processed attachments with "
. join(', ', @function_names) . "."
if scalar @function_names;
}

return $result;
}

sub delete_email_imports {
my ($self, $email_import_ids_to_delete) = @_;

my @not_found_email_import_ids;
my @deleted_email_import_ids;
foreach my $email_import_id (@$email_import_ids_to_delete) {
my $email_import = SL::DB::Manager::EmailImport->find_by(id => $email_import_id);
unless ($email_import) {
push @not_found_email_import_ids, $email_import_id;
next;
}
$email_import->delete(cascade => 1);
push @deleted_email_import_ids, $email_import_id;
}

my $result = "";

$result .= "Deleted email import(s): "
. join(', ', @deleted_email_import_ids) . "."
if scalar @deleted_email_import_ids;

$result .= "Could not find email import(s): "
. join(', ', @not_found_email_import_ids) . " for deletion."
if scalar @not_found_email_import_ids;

return $result;
}

sub run {
my ($self, $job_obj) = @_;
$self->{job_obj} = $job_obj;

my $data = $job_obj->data_as_hash;

my $record_type = $data->{record_type};
my $loaded_config = $::lx_office_conf{"record_emails_imap/record_type/$record_type"}
|| $::lx_office_conf{record_emails_imap}
|| {};

# overwrite with background job data
$loaded_config->{$_} = $data->{$_} for keys %{$data};
my @config_params = %{$loaded_config};

my %config = validate_with(
params => \@config_params,
spec => {
folder => {
type => SCALAR,
optional => 1,
},
record_type => {
optional => 1,
default => 'catch_all',
callbacks => {
'valid record type' => sub {
my $valid_record_types = SL::DB::EmailJournal->meta->{columns}->{record_type}->{check_in};
unless (any {$_[0] eq $_} @$valid_record_types) {
die "record_type '$_[0]' is not valid. Possible values:\n- " . join("\n- ", @$valid_record_types);
}
},
},
},
process_imported_emails => {
type => SCALAR | ARRAYREF,
optional => 1,
callbacks => {
'function is implemented' => sub {
foreach my $function_name (ref $_[0] eq 'ARRAY' ? @{$_[0]} : ($_[0])) {
!!SL::Helper::EmailProcessing->can_function($function_name) or
die "Function '$function_name' not implemented in SL::Helper::EmailProcessing";
}
1;
}
}
},
processed_imap_flag => { type => SCALAR, optional => 1, },
not_processed_imap_flag => { type => SCALAR, optional => 1, },
email_import_ids_to_delete => { type => ARRAYREF, optional => 1, },
# email config
hostname => { type => SCALAR, },
port => { type => SCALAR, optional => 1},
ssl => { type => BOOLEAN, },
username => { type => SCALAR, },
password => { type => SCALAR, },
base_folder => { type => SCALAR, optional => 1},
},
called => "data filed in Background Job or kivitendo.conf in [record_emails_imap] with type $record_type",
);

my @results;
if (scalar $config{email_import_ids_to_delete}) {
push @results, $self->delete_email_imports($config{email_import_ids_to_delete});
}

push @results, $self->sync_record_email_folder(\%config);

return join("\n", grep { $_ ne ''} @results);
}

1;

__END__

=encoding utf8

=head1 NAME

SL::BackgroundJob::ImportPurchaseInvoiceEmails - Background job for syncing
emails from a folder for records.

=head1 SYNOPSIS

This background job syncs emails from a folder for records. The emails are
imported as email journals and can be processed with functions from
SL::Helper::EmailProcessing.

=head1 CONFIGURATION

In kivitendo.conf the settings for the IMAP server can be specified. The
default config is under [record_emails_imap]. The config for a specific record
type is under [record_emails_imap/record_type/<record_type>]. The config for a
specific record type overwrites the default config. The data fields can
overwrite single configration values.

=over 4

=item record_type

The record type to set for each imported email journal. This is used to get
a specific config under [record_emails_imap/record_type/<record_type>]. The
default value is C<catch_all>

=item folder

The folder to sync emails from. Sub folders are separated by a forward slash,
e.g. 'INBOX/Archive'. Subfolders are not synced.

=back

=head1 BUGS

Nothing here yet.

=head1 AUTHOR

Tamino Steinert E<lt>[email protected]<gt>

=cut
23 changes: 21 additions & 2 deletions SL/BackgroundJob/SyncEmailFolder.pm
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@ use warnings;

use parent qw(SL::BackgroundJob::Base);

use Params::Validate qw(:all);

use SL::IMAPClient;
use SL::DB::Manager::EmailImport;

sub sync_email_folder {
my ($self) = @_;
my $folder = $self->{job_obj}->data_as_hash->{folder};

my $imap_client = SL::IMAPClient->new();
my $imap_client = SL::IMAPClient->new(%{$::lx_office_conf{imap_client}});

my $email_import = $imap_client->update_emails_from_folder($folder);
my $email_import = $imap_client->update_emails_from_folder(
folder => $folder
);
return unless $email_import;

return "Created email import: " . $email_import->id;
Expand Down Expand Up @@ -42,6 +46,21 @@ sub delete_email_imports {
sub run {
my ($self, $job_obj) = @_;
$self->{job_obj} = $job_obj;
my @bj_data = $job_obj->data_as_hash;
validate_with(
params => \@bj_data,
spec => {
folder => {
type =>
SCALAR, optional => 1
},
email_import_ids_to_delete => {
type => ARRAYREF,
optional => 1,
}
},
called => "data filed in Background Job",
);

my @results;
push @results, $self->delete_email_imports();
Expand Down
3 changes: 2 additions & 1 deletion SL/Controller/Base.pm
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ sub send_file {
}

my $content_type = $params{type} || 'application/octet_stream';
my $content_disposition = $params{content_disposition} || 'attachment';
my $attachment_name = $params{name} || (!ref($file_name_or_content) ? $file_name_or_content : '');
$attachment_name =~ s:.*//::g;

Expand All @@ -165,7 +166,7 @@ sub send_file {
$self->js->render unless $params{js_no_render};
} else {
print $::form->create_http_response(content_type => $content_type,
content_disposition => 'attachment; filename="' . $attachment_name . '"',
content_disposition => $content_disposition . '; filename="' . $attachment_name . '"',
content_length => $size);

if (!ref $file_name_or_content) {
Expand Down
Loading