MT#5879 Send invoice to email functionality draft.

Todo:
Add invoice email into db. Send email functionality.
Conditional fields in HFH.
Expand accordions on demand.
ipeshinskaya/InvoiceTemplate5
Irina Peshinskaya 11 years ago committed by Victor Seva
parent 59b932b9a4
commit 7465db49d4

@ -16,7 +16,8 @@ use NGCP::Panel::Utils::Message;
use NGCP::Panel::Form::Invoice::Template;
use NGCP::Panel::Form::Invoice::Generate;
use NGCP::Panel::Form::Invoice::Basic;
use NGCP::Panel::Form::Invoice::Send;
#use NGCP::Panel::Form::Invoice::Basic;
use NGCP::Panel::Model::DB::InvoiceTemplate;
use NGCP::Panel::Utils::InvoiceTemplate;
@ -323,11 +324,19 @@ sub invoice_generate :Chained('base') :PathPart('generate') :Args(0) {
#backend => $backend,
#client_contract_ajax_src => $c->uri_for_action( '/invoice/ajax_datatables_data', [ $self->provider_id, 'invoice_list_data' ],
);
#todo: try to move it tp frp, template. really this is view responsibility
#cpan is out again
#if(!$in->{client_contract_id}){
$validator->field('client_contract_id')->ajax_src(
$c->uri_for_action(
'/invoice/ajax_datatables_data',
[ $in->{provider_id}, 'provider_client_list' ] ,
)->as_string());
#}else{
# $validator->field('client_contract_id',$validator->field('client_contract_id_hidden'));
# $validator->field('client_contract_id')->name('client_contract_id');
#}
$validator->remove_undef_in($in);
#need to think how to automate it - maybe through form showing param through args? what about args for uri_for_action?
$validator->action( $c->uri_for_action('invoice/invoice_generate',[$in->{provider_id}]) );
@ -460,6 +469,74 @@ sub parse_invoice_period :Private{
$in->{etime} = $parser->parse_datetime($in->{end})->truncate(to => 'day')->add(days => 1)->subtract(seconds => 1);
}
}
sub invoice_send :Chained('base') :PathPart('send') :Args(0) {
my ($self, $c) = @_;
$c->log->debug($c->action);
my($validator,$backend,$in,$out);
$backend = NGCP::Panel::Model::DB::InvoiceTemplate->new( schema => $c->model('DB') );
#from parameters
$in = $c->request->parameters;
$in->{provider_id} = $c->stash->{provider}->id;
$validator = NGCP::Panel::Form::Invoice::Send->new(
backend => $backend,
);
$validator->remove_undef_in($in);
#need to think how to automate it - maybe through form showing param through args? what about args for uri_for_action?
$validator->action( $c->uri_for_action('invoice/invoice_send',[$in->{provider_id}]) );
$validator->name( 'invoice_send' );#from parameters
my $posted = exists $in->{submitid};
$c->log->debug("posted=$posted;");
if($in->{invoice_id} && !$in->{email}){
if(my $invoice_contact = $backend->getInvoiceContact(%$in)){
$in->{email} = $invoice_contact->get_column('email');
}
}
#todo: validate that customer is not terminated and is sip/pbx account
$validator->process(
posted => $posted,
params => $in,
#no edit supposed for invoice - just creation and deletion, so item from DB is not used
item => $in,
);
my $in_validated = $validator->fif;
if($posted){
$c->log->debug("validated=".$validator->validated.";");
if($validator->validated) {
#logic here
try {
$c->flash(messages => [{type => 'success', text => $c->loc(
'Invoice generated'
) }]);
} catch($e) {
NGCP::Panel::Utils::Message->error(
c => $c,
error => $e,
desc => $c->loc(
'Failed to generate invoice.'
),
);
}
$c->stash( messages => $c->flash->{messages} );
$c->stash( template => 'helpers/ajax_messages.tt' );
}else{
$c->response->headers->header( 'X-Form-Status' => 'error' );
}
}
if(!$validator->validated){
$c->stash( m => {create_flag => !$in->{invoice_id}} );
$c->stash( form => $validator );
$c->stash( template => 'invoice/invoice_send_form.tt' );
}
$c->detach( $c->view("SVG") );#to the sake of nowrapper
}
sub template_base :Chained('base') :PathPart('template') :CaptureArgs(0) {
my ($self, $c) = @_;

@ -1,12 +1,7 @@
package NGCP::Panel::Form::Invoice::Generate;
#use Sipwise::Base;
use HTML::FormHandler::Moose;
#extends qw/HTML::FormHandler NGCP::Panel::Form::ValidatorBase/;
extends 'NGCP::Panel::Form::ValidatorBase';
#extends qw/NGCP::Panel::Form::ValidatorBase HTML::FormHandler::Field::Compound/;
#extends 'HTML::FormHandler::Field::Compound';
#use Moose::Util::TypeConstraints;
use DateTime;
use DateTime::Format::Strptime;
@ -21,6 +16,7 @@ has_field 'client_contract_id' => (
is => 'rw',
type => '+NGCP::Panel::Field::DataTable',
label => 'Contract',
name => 'client_contract_id',
do_label => 0,
do_wrapper => 0,
required => 1,
@ -28,28 +24,10 @@ has_field 'client_contract_id' => (
#we will set it in controller
#ajax_src => $c->uri_for_action( '/invoice/ajax_datatables_data', [ $self->provider_id, 'invoice_list_data' ],
ajax_src => '',
# { name => 'contracts.id', title => c.loc('Client Contract Id'), search => 1},
# { name => 'contracts.external_id', title => c.loc('External #'), search => 1 },
# { name => 'email', title => c.loc('Contact Email'), search => 1 },
# { name => 'contracts.billing_mappings.billing_profile.name', title => c.loc('Billing Profile'), search => 1 },
# { name => 'contracts.billing_mappings.product.name', title => c.loc('Product'), search => 1 },
# { name => 'contracts.status', title => c.loc('Status'), search => 1 },
table_titles => ['Contract Id', 'First Name', 'Last Name', 'Email'],
table_fields => ['contracts.id', 'firstname', 'lastname', 'email'],
);
#has_field 'contract.id' => (
# type => '+NGCP::Panel::Field::DataTable',
# label => 'Client',
# do_label => 0,
# do_wrapper => 0,
# required => 1,
# template => 'helpers/datatables_field.tt',
# ajax_src => '/contact/ajax_noreseller',
# table_titles => ['#', 'First Name', 'Last Name', 'Email'],
# table_fields => ['id', 'firstname', 'lastname', 'email'],
#);
has_field 'start' => (
type => '+NGCP::Panel::Field::DatePicker',
label => 'Start Date',
@ -71,10 +49,10 @@ has_field 'save' => (
do_label => 0,
);
#has_field 'client_contract_id' => (
# type => 'Hidden',
# required => 1,
#);
has_field 'client_contract_id_hidden' => (
type => 'Hidden',
required => 1,
);
has_block 'fields' => (
tag => 'div',

@ -0,0 +1,59 @@
package NGCP::Panel::Form::Invoice::Send;
use HTML::FormHandler::Moose;
extends 'NGCP::Panel::Form::ValidatorBase';
has_field 'submitid' => ( type => 'Hidden' );
has '+widget_wrapper' => ( default => 'Bootstrap' );
sub build_render_list {[qw/submitid fields actions/]}
sub build_form_element_class { [qw/form-horizontal/] }
has_field 'email' => (
type => 'Text',
label => 'Emails',
required => 1,
);
has_field 'save' => (
type => 'Button',
value => 'Send',
element_class => [qw/btn btn-primary/],
do_label => 0,
);
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/email/],
);
has_block 'actions' => (
tag => 'div',
class => [qw/modal-footer/],
render_list => [qw/save/],
);
1;
=head1 NAME
NGCP::Panel::Form::InvoiceTemplate
=head1 DESCRIPTION
Form to modify an invoice template.
=head1 METHODS
=head1 AUTHOR
Irina Peshinskaya
=head1 LICENSE
This library is free software. You can redistribute it and/or modify
it under the same terms as Perl itself.
=cut
# vim: set tabstop=4 expandtab:

@ -367,9 +367,21 @@ sub getInvoice{
my (%params) = @_;
my ($invoice_id) = @params{qw/invoice_id/};
return $self->schema->resultset('invoices')->search({
'id' => $invoice_id,
'me.id' => $invoice_id,
}, undef )->first;
}
sub getInvoiceContact{
my $self = shift;
my (%params) = @_;
my ($invoice_id) = @params{qw/invoice_id/};
return $self->schema->resultset('invoices')->search({
'me.id' => $invoice_id,
}, {
'+select' => ['contact.email'],
'+as' => ['email'],
'join' => [ {'contract_balances' => { 'contract' => 'contact' } } ],
} )->first;
}
sub deleteInvoice{
my $self = shift;
my (%params) = @_;
@ -546,7 +558,7 @@ sub checkResellerClientContact{
if($in->{client_contact_id} && $in->{provider_id}){
if(my $contact = $self->schema->resultset('contacts')->search({
'reseller_id' => $in->{provider_id},
'id' => $in->{client_contact_id},
'me.id' => $in->{client_contact_id},
})->first){
$res = $contact->get_column('id');
}
@ -561,7 +573,7 @@ sub checkResellerInvoice{
if($in->{invoice_id} && $in->{provider_id}){
if(my $invoice = $self->schema->resultset('invoices')->search({
'contact.reseller_id' => $in->{provider_id},
'id' => $in->{client_contact_id},
'me.id' => $in->{invoice_id},
},{
'join' => { 'contract_balances' => { 'contract' => 'contact' }},
})->first){
@ -581,7 +593,7 @@ sub checkResellerInvoiceTemplate{
#$in->{c}->log->debug("checkResellerInvoiceTemplate: tt_id=".$in->{tt_id}.";provider_id=".$in->{provider_id}.";");
if(my $tt = $self->schema->resultset('invoice_templates')->search({
'reseller_id' => $in->{provider_id},
'id' => $in->{tt_id},
'me.id' => $in->{tt_id},
})->first){
$res = $tt->get_column('id');
}

@ -0,0 +1,128 @@
package NGCP::Panel::Utils::TTEmailer;
sub send_email{
my $self = shift;
my ($email,$hr_tmpl,$hr_vars) = @_;
my ($email,$subject,$text,$from,$type,$headers,$images,$binatt) = @$hr_mail{qw/email subject text from type headers images binatt/};
my $cfg = Knetrix::Config->get;
# If we don't specify the email body then fetch it from t_$view_${action}email.tt2
if(!$text){
$text = '';
$hr_tmpl->{$_} ||= ${$self->{OUT_DATA}{request}}{$_} foreach qw/module sub_module view action/;
if(!$hr_tmpl->{tmplnamestrict}){
$hr_tmpl->{action} .= 'email';
}
$hr_tmpl->{error} = 0;
my $stc = {};
my $tt2 = Knetrix::Template->instance() || throw Knetrix::Error -text=>"Missing tt2 obj";
if(ref $hr_vars eq 'HASH'){
use Hash::Merge ();
$hr_vars = Hash::Merge::merge $self->{OUT_DATA} , $hr_vars;
} else {
$hr_vars = $self->{OUT_DATA};
}
(@{$hr_vars->{config}}{qw/http httpdata data/},@{$hr_vars->{tmpl_hash}}{qw/module sub_module/})
= ($cfg->dir_config('http'),$cfg->dir_config('httpdata'),$cfg->dir_config('systemdata'),@$hr_tmpl{qw/module sub_module/});
$hr_vars->{tmpl_req} = $hr_tmpl;
# Some funky magic to access variables inside the template
$hr_vars->{import} ||= $stc;
my $importold = $Template::Stash::HASH_OPS->{ import } if defined $Template::Stash::HASH_OPS->{ import };
$Template::Stash::HASH_OPS->{ import } = sub { $stc = $_[0] };
$log->error(Dumper $hr_tmpl);
$log->error(Dumper $self->{OUT_DATA}{request});
$hr_tmpl = Knetrix::Template::Magic->wand($hr_tmpl);
my $ok = $tt2->process($hr_tmpl,$hr_vars,\$text) || $log->error($tt2->error());
$Template::Stash::HASH_OPS->{ import } = $importold if defined $importold;
$subject ||= $stc->get('subject');
$type ||= $stc->get('mimetype');
$from ||= $stc->get('from');
}
# If from isn't specified fetch it from knetrix.xml or use support@envisionext.com
if(!$from){
$from = $cfg->dir_config('from','email') || 'support@envisionext.com';
}
# Extract the Images from the template/mail body
$images ||= {};
sub collect_images{
my ($q1,$image,$q2,$images) = @_;
$log->error(Dumper \@_);
my($path,$filename) = $image =~/(.*?)([^\/]+\/?)$/;
$log->error("path is $path filename is $filename");
$images->{$filename} = $path;
return $q1.$filename.$q2;
}
$text =~s/(['"]cid:)([^'"]+)(['"])/collect_images($1,$2,$3,$images)/ge;
my $imagescnt = scalar keys(%$images);
if($binatt){
ref $binatt eq 'HASH' and $binatt = [$binatt];
ref $binatt eq 'ARRAY' and $imagescnt += scalar @$binatt;
}
$log->debug("imagescnt=",$imagescnt,"; text=",$text,"images=",Dumper $images);
# Set the mail type based on the existence of any images
my %msgadd;
if ($imagescnt) {
%msgadd = ( Type => 'multipart/related' );
} else {
%msgadd = ( Type => $type?$type:'text/html',
Data => $text,
);
}
# Setup the mail object
my $msg = MIME::Lite->new( To => $email,
From => $from,
Subject => $subject,
%msgadd,
);
# Setup a debug email for the postmaster
my $debugmsg = MIME::Lite->new( To => $cfg->dir_config('postmaster','email') || 'ric@envisionext.com',
From => $from,
Subject => $subject,
%msgadd,
);
# Attach the body and any images
if($imagescnt){
$msg->attach( Data => $text,
Type => 'text/html',
);
$debugmsg->attach( Data => $text."<pre>Util::send_email\n\n\nTo:$email;".(Dumper([$hr_mail,$hr_vars]))."\n</pre>",
Type => 'text/html',
);
# Attach the images
my $datadir = $cfg->dir_config('staticdata');
while( my ($img,$path) = each(%$images) ){
my ($ext) = $img=~/[^\.]+\.(.+)$/;
$msg->attach( Type => $mimetypes{$ext},
Id => $img,
Path => join '/', $datadir,$path,$img,
);
$debugmsg->attach( Type => $mimetypes{$ext},
Id => $img,
Path => join '/', $datadir,$path,$img,
);
}
foreach (_afy $binatt){
$msg->attach( %$_);
$debugmsg->attach( %$_);
}
}
my $res;
print STDERR Dumper $msg;
if(!$hr_mail->{dontsend}){
my @params = split(':',$cfg->dir_config('server','email'));
$res = $msg->send(@params) unless $cfg->dir_config('safemode','email');;
$debugmsg->send(@params) if $cfg->dir_config('debug','email');
}else{
$res = 1;
}
return ($res,$msg,$debugmsg)
}
1;

@ -35,6 +35,7 @@
[%m.close_target_direct%]
[%ELSIF m.ajax_load%]
$('#mod_edit').modal('hide');
$('#mod_edit').parent('div').html('');
[%ELSE%]
console.log("redirecting to [% m.close_target ? m.close_target : c.uri_for() %]");
window.location.href="[% m.close_target ? m.close_target : c.uri_for() %]";
@ -63,6 +64,7 @@
if( status != 'error' ){
//form object is necessary to compose uri for refresh
refreshAjaxList( '[%m.ajax_list_refresh%]', form.serializeObject() );
$('#mod_edit').parent('div').html('');
}
[%END%]
[%m.ajax_callback%]

@ -0,0 +1,2 @@
[%m.name = "Send Invoice"%]
[%PROCESS 'helpers/ajax_form_modal.tt'-%]

@ -30,7 +30,7 @@
[%messages.unshift( c.loc('Reseller is <b>[_1]</b>', reseller.first.status) ); %]
[% END -%]
<div class="row" id="messages">
[%#here can be just initial messages state, but now it is th same with ajax messages, so there is no sense in repetition-%]
[%#here can be just initial messages state, but now it is the same with ajax messages, so there is no sense in repetition-%]
[%PROCESS 'helpers/ajax_messages.tt' -%]
</div>
<script>
@ -62,7 +62,7 @@ function applyClientFilter(table,tr,contact_id){
localStorage.setItem('ngcp_dt.invoice_list_data_ajax_table.paramsJSON',JSON.stringify({'client_contact_id':contact_id}));
refreshInvoicesTable();
}
function showGenerateForm(contract_id, contact_id){
function generateInvoiceForm(contract_id, contact_id){
fetch_into(
'invoice_generate_form',
'[%c.uri_for_action("/invoice/invoice_generate", [ provider.id])%]',
@ -70,6 +70,26 @@ function showGenerateForm(contract_id, contact_id){
function(){modalFormScript();}
);
}
function sendInvoiceForm(invoice_id){
fetch_into(
'invoice_send_form',
'[%c.uri_for_action("/invoice/invoice_send", [ provider.id])%]',
'item=invoice_send&invoice_id='+invoice_id,
function(){modalFormScript();}
);
}
function deleteInvoice(invoice_id,table_name){
fetch_into('messages',
'[%c.uri_for_action("/invoice/invoice_delete", [ provider.id ] )%]',
'invoice_id='+invoice_id,
function(){$('#'+table_name).DataTable().fnDraw();}
);
}
//$(document).ready(function() {
// alert($('#toggle-accordions'));
// $('#toggle-accordions').trigger('click');
// $('#toggle-accordions').click();
//});
</script>
<div class="ngcp-separator"></div>
@ -101,7 +121,7 @@ function showGenerateForm(contract_id, contact_id){
{
name => c.loc('Generate invoice'),
uri => 'javascript:void(0);',
onclick => "showGenerateForm('+full.contracts_id+');void(0);",
onclick => "generateInvoiceForm('+full.contracts_id+');void(0);",
class => 'btn-small btn-primary',
icon => 'icon-star'
},
@ -156,19 +176,27 @@ function showGenerateForm(contract_id, contact_id){
{ name => 'contract_balances.free_time_balance', title => c.loc('Free Time balance')},
];
helper.dt_buttons = [
{
name = c.loc('Delete'),
uri = "javascript:fetch_into(\\'messages\\', \\'" _ c.uri_for_action('/invoice/invoice_delete', [ provider.id ] ) _ "\\',\\'invoice_id='+full.contract_balances_invoice_id+'\\',function(){\$(\\'#" _ helper.identifier _ "_table\\').DataTable().fnDraw();});void(0);",
class = 'btn-small btn-secondary',
icon = 'icon-trash',
attributes= ' cancel-hide="1"'
},
{
name = c.loc('View invoice PDF'),
uri = "javascript:window.open(\\'" _ c.uri_for_action('/invoice/invoice_data', [ provider.id ] ) _ "/' + full.contract_balances_invoice_id + '\\',\\'_blank\\');void(0);",
class = 'btn-small btn-primary',
icon = 'icon-edit'
},
{
name = c.loc('Send invoice'),
uri => 'javascript:void(0);',
onclick => "sendInvoiceForm('+full.contract_balances_invoice_id+');void(0);",
class => 'btn-small btn-primary',
icon => 'icon-star'
attributes= ' cancel-hide="1"'
},
{
name = c.loc('Delete'),
uri = "javascript:deleteInvoice(\\''+full.contract_balances_invoice_id+'\\',\\'" _ helper.identifier _ "\\');void(0);",
class = 'btn-small btn-secondary',
icon = 'icon-trash',
attributes= ' cancel-hide="1"'
},
];
helper.ajax_uri = c.uri_for_action( '/invoice/ajax_datatables_data', [ provider.id, 'invoice_list_data' ] ) ;
initHelperAuto();
@ -263,3 +291,6 @@ oTable.fnClose();
<div id="invoice_generate_form">
</div>
<div id="invoice_send_form">
</div>

Loading…
Cancel
Save