TT#17848 Separate invoice creation method

Change-Id: I6b0701ccb80601c7171e0891950276e9d375b429
changes/98/14198/12
Irina Peshinskaya 8 years ago
parent 07b9f8dcae
commit 5ef25afdbe

@ -147,6 +147,7 @@ sub create :Chained('inv_list') :PathPart('create') :Args() :Does(ACL) :ACLDetac
my $params = {};
$params = merge($params, $c->session->{created_objects});
my $schema = $c->model('DB');
my $form;
$form = NGCP::Panel::Form::Invoice::Invoice->new(ctx => $c);
$form->process(
@ -162,228 +163,38 @@ sub create :Chained('inv_list') :PathPart('create') :Args() :Does(ACL) :ACLDetac
);
if($posted && $form->validated) {
try {
my $schema = $c->model('DB');
my $contract_id = $form->values->{contract}{id};
delete $form->values->{customer};
my $tmpl_id = $form->values->{template}{id};
delete $form->values->{template};
my $period = delete $form->values->{period};
my($customer,$tmpl,$stime,$etime,$invoice_data);
($contract_id,$customer,$tmpl,$stime,$etime,$invoice_data) = NGCP::Panel::Utils::Invoice::check_invoice_data($c, {
contract_id => $contract_id,
tmpl_id => $tmpl_id,
period_start => undef,
period_end => undef,
period => $period,
});
$schema->set_transaction_isolation('READ COMMITTED');
$schema->txn_do(sub {
my $contract_id = $form->values->{contract}{id};
my $customer_rs = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c, contract_id => $contract_id);
my $customer = $customer_rs->find({ 'me.id' => $contract_id });
unless($customer) {
NGCP::Panel::Utils::Message::error(
c => $c,
error => "invalid contract_id $contract_id",
desc => $c->loc('Customer not found'),
);
die;
}
delete $form->values->{contract};
$form->values->{contract_id} = $contract_id;
my $tmpl_id = $form->values->{template}{id};
delete $form->values->{template};
my $tmpl = $schema->resultset('invoice_templates')->search({
id => $tmpl_id,
NGCP::Panel::Utils::Invoice::create_invoice($c,{
contract_id => $contract_id,
customer => $customer,
stime => $stime,
etime => $etime,
tmpl => $tmpl,
invoice_data => $invoice_data,
});
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$tmpl = $tmpl->search({
reseller_id => $c->user->reseller_id,
});
}
$tmpl = $tmpl->first;
unless($tmpl) {
NGCP::Panel::Utils::Message::error(
c => $c,
error => "invalid template id $tmpl_id",
desc => $c->loc('Invoice template not found'),
);
die;
}
unless($tmpl->data) {
NGCP::Panel::Utils::Message::error(
c => $c,
error => "invalid template id $tmpl_id, data is empty",
desc => $c->loc('Invoice template does not have an SVG stored yet'),
);
die;
}
unless($customer->contact->reseller_id == $tmpl->reseller_id) {
NGCP::Panel::Utils::Message::error(
c => $c,
error => "template id ".$tmpl->id." has different reseller than contract id $contract_id",
desc => $c->loc('Template and customer must belong to same reseller'),
);
die;
}
my $stime = NGCP::Panel::Utils::DateTime::from_string(
delete $form->values->{period}
)->truncate(to => 'month');
my $etime = $stime->clone->add(months => 1)->subtract(seconds => 1);
#this has to be refactored - select a contract balance instead of a "period"
my $balance = NGCP::Panel::Utils::ProfilePackages::get_contract_balance(c => $c,
contract => $customer,
stime => $stime,
etime => $etime,);
$stime = $balance->start;
$etime = $balance->end;
my $bm_actual = NGCP::Panel::Utils::ProfilePackages::get_actual_billing_mapping(c => $c, contract => $customer, now => $balance->start);
my $billing_profile = $bm_actual->billing_mappings->first->billing_profile;
my $zonecalls = NGCP::Panel::Utils::Contract::get_contract_zonesfees(
c => $c,
contract_id => $contract_id,
stime => $stime,
etime => $etime,
in => 0,
out => 1,
group_by_detail => 1,
);
my $calllist_rs = NGCP::Panel::Utils::Contract::get_contract_calls_rs(
c => $c,
customer_contract_id => $contract_id,
stime => $stime,
etime => $etime,
);
my $calllist = [ map {
my $call = {$_->get_inflated_columns};
$call->{start_time} = $call->{start_time}->epoch;
$call->{destination_user_in} =~s/%23/#/g;
#$call->{destination_user_in} = encode_entities($call->{destination_user_in}, '<>&"#');
$call->{source_customer_cost} += 0.0; # make sure it's a number
NGCP::Panel::Utils::CallList::suppress_cdr_fields($c,$call,$_);
} $calllist_rs->all ];
#my $billing_mapping = $customer->billing_mappings->find($customer->get_column('bmid'));
#my $billing_profile = $billing_mapping->billing_profile;
#try {
# $balance = NGCP::Panel::Utils::Contract::get_contract_balance(
# c => $c,
# profile => $billing_profile,
# contract => $customer,
# stime => $stime,
# etime => $etime
# );
#} catch($e) {
# NGCP::Panel::Utils::Message::error(
# c => $c,
# error => $e,
# desc => $c->loc('Failed to get contract balance.'),
# );
# die;
#}
my $invoice_amounts = NGCP::Panel::Utils::Invoice::get_invoice_amounts(
customer_contract => {$customer->get_inflated_columns},
billing_profile => {$billing_profile->get_inflated_columns},
contract_balance => {$balance->get_inflated_columns},
);
@{$form->values}{qw/amount_net amount_vat amount_total/} = @$invoice_amounts{qw/amount_net amount_vat amount_total/};
# generate tmp serial here, derive one from after insert
$form->values->{serial} = "tmp".time.int(rand(99999));
$form->values->{data} = undef;
#maybe inflation should be applied? Generation failed here, although the latest schema applied.
$form->values->{period_start} = $stime->ymd.' '. $stime->hms;
$form->values->{period_end} = $etime->ymd.' '. $etime->hms;
my $invoice;
try {
$invoice = $schema->resultset('invoices')->create($form->values);
} catch($e) {
NGCP::Panel::Utils::Message::error(
c => $c,
error => $e,
desc => $c->loc('Failed to save invoice meta data.'),
);
die;
}
#sprintf("INV%04d%02d%07d", $stime->year, $stime->month, $invoice->id);
#to make it unified for web and cron script
my $serial = NGCP::Panel::Utils::Invoice::get_invoice_serial($c,{
invoice=>{
period_start => $stime,
period_end => $etime,
id => $invoice->id,
}});
my $svg = $tmpl->data;
utf8::decode($svg);
my $t = NGCP::Panel::Utils::InvoiceTemplate::get_tt();
my $out = '';
my $pdf = '';
my $vars = {};
# TODO: index 170 seems the upper limit here, then the calllist breaks
$vars->{rescontact} = { $customer->contact->reseller->contract->contact->get_inflated_columns };
$vars->{customer} = { $customer->get_inflated_columns };
$vars->{custcontact} = { $customer->contact->get_inflated_columns };
$vars->{billprof} = { $billing_profile->get_inflated_columns };
NGCP::Panel::Utils::Invoice::prepare_contact_data($vars->{billprof});
NGCP::Panel::Utils::Invoice::prepare_contact_data($vars->{custcontact});
NGCP::Panel::Utils::Invoice::prepare_contact_data($vars->{rescontact});
$vars->{invoice} = {
period_start => $stime,
period_end => $etime,
serial => $serial,
amount_net => $form->values->{amount_net},
amount_vat => $form->values->{amount_vat},
amount_total => $form->values->{amount_total},
};
$vars->{calls} = $calllist,
$vars->{zones} = {
totalcost => $balance->cash_balance_interval,
data => [ values(%{ $zonecalls }) ],
};
try {
$t->process(\$svg, $vars, \$out) || do {
my $error = $t->error();
my $msg = "error processing template, type=".$error->type.", info='".$error->info."'";
NGCP::Panel::Utils::Message::error(
c => $c,
log => $msg,
desc => $c->loc('Failed to render template. Type is [_1], info is [_2].', $error->type, $error->info),
);
NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/invoice'));
return;
};
NGCP::Panel::Utils::InvoiceTemplate::preprocess_svg(\$out);
NGCP::Panel::Utils::InvoiceTemplate::svg_pdf($c, \$out, \$pdf);
} catch($e) {
NGCP::Panel::Utils::Message::error(
c => $c,
log => $e,
desc => $c->loc('Failed to render invoice'),
);
NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/invoice'));
return;
}
$invoice->update({
serial => $serial,
data => $pdf,
});
NGCP::Panel::Utils::Message::info(
c => $c,
cname => 'create',
log => $vars->{invoice},
desc => $c->loc('Invoice #[_1] successfully created', $invoice->id),
);
});
} catch($e) {
NGCP::Panel::Utils::Message::error(
c => $c,
c => $c,
error => $e,
desc => $c->loc('Failed to create invoice.'),
desc => $c->loc('Failed to create invoice'),
);
}
NGCP::Panel::Utils::Navigation::back_or($c, $c->uri_for('/invoice'));

@ -1,8 +1,14 @@
package NGCP::Panel::Utils::Invoice;
use Geography::Countries qw/country/;
use Sipwise::Base;
use NGCP::Panel::Utils::ProfilePackages;
use NGCP::Panel::Utils::InvoiceTemplate;
use NGCP::Panel::Utils::Contract;
use NGCP::Panel::Utils::Message;
use NGCP::Panel::Utils::CallList;
use HTML::Entities;
use Geography::Countries qw/country/;
use HTTP::Status qw(:constants);
sub get_invoice_amounts{
my(%params) = @_;
@ -22,11 +28,13 @@ sub get_invoice_amounts{
$invoice->{amount_total} = $invoice->{amount_net} + $invoice->{amount_vat};
return $invoice;
}
sub get_invoice_serial{
my($c,$params) = @_;
my($invoice) = @$params{qw/invoice/};
return sprintf("INV%04d%02d%07d", $invoice->{period_start}->year, $invoice->{period_start}->month, $invoice->{id});
}
sub prepare_contact_data{
my($contact) = @_;
$contact->{country} = country($contact->{country} || 0);
@ -36,5 +44,223 @@ sub prepare_contact_data{
#passed by reference
#return $contact;
}
sub create_invoice{
my($c,$params) = @_;
my($contract_id,$customer,$stime,$etime,$tmpl,$invoice_data) = @$params{qw/contract_id customer stime etime tmpl invoice_data/};
my $invoice;
my $schema = $c->model('DB');
#this has to be refactored - select a contract balance instead of a "period"
my $balance = NGCP::Panel::Utils::ProfilePackages::get_contract_balance(
c => $c,
contract => $customer,
stime => $stime,
etime => $etime,);
$stime = $balance->start;
$etime = $balance->end;
my $bm_actual = NGCP::Panel::Utils::ProfilePackages::get_actual_billing_mapping(
c => $c,
contract => $customer,
now => $balance->start);
my $billing_profile = $bm_actual->billing_mappings->first->billing_profile;
my $zonecalls = NGCP::Panel::Utils::Contract::get_contract_zonesfees(
c => $c,
contract_id => $contract_id,
stime => $stime,
etime => $etime,
in => 0,
out => 1,
group_by_detail => 1,
);
my $calllist_rs = NGCP::Panel::Utils::Contract::get_contract_calls_rs(
c => $c,
customer_contract_id => $contract_id,
stime => $stime,
etime => $etime,
);
my $calllist = [ map {
my $call = {$_->get_inflated_columns};
$call->{start_time} = $call->{start_time}->epoch;
$call->{destination_user_in} =~s/%23/#/g;
#$call->{destination_user_in} = encode_entities($call->{destination_user_in}, '<>&"#');
$call->{source_customer_cost} += 0.0; # make sure it's a number
NGCP::Panel::Utils::CallList::suppress_cdr_fields($c,$call,$_);
} $calllist_rs->all ];
#my $billing_mapping = $customer->billing_mappings->find($customer->get_column('bmid'));
#my $billing_profile = $billing_mapping->billing_profile;
#try {
# $balance = NGCP::Panel::Utils::Contract::get_contract_balance(
# c => $c,
# profile => $billing_profile,
# contract => $customer,
# stime => $stime,
# etime => $etime
# );
#} catch($e) {
# NGCP::Panel::Utils::Message::error(
# c => $c,
# error => $e,
# desc => $c->loc('Failed to get contract balance.'),
# );
# die;
#}
my $invoice_amounts = get_invoice_amounts(
customer_contract => {$customer->get_inflated_columns},
billing_profile => {$billing_profile->get_inflated_columns},
contract_balance => {$balance->get_inflated_columns},
);
@{$invoice_data}{qw/amount_net amount_vat amount_total/} = @$invoice_amounts{qw/amount_net amount_vat amount_total/};
# generate tmp serial here, derive one from after insert
$invoice_data->{serial} = "tmp".time.int(rand(99999));
$invoice_data->{data} = undef;
#maybe inflation should be applied? Generation failed here, although the latest schema applied.
$invoice_data->{period_start} = $stime->ymd.' '. $stime->hms;
$invoice_data->{period_end} = $etime->ymd.' '. $etime->hms;
try {
$invoice = $schema->resultset('invoices')->create($invoice_data);
} catch($e) {
die {
showdetails => $c->loc('Failed to save invoice meta data.'),
error => $e,
httpcode => HTTP_UNPROCESSABLE_ENTITY,
};
}
#sprintf("INV%04d%02d%07d", $stime->year, $stime->month, $invoice->id);
#to make it unified for web and cron script
my $serial = NGCP::Panel::Utils::Invoice::get_invoice_serial($c,{
invoice=>{
period_start => $stime,
period_end => $etime,
id => $invoice->id,
}});
my $svg = $tmpl->data;
utf8::decode($svg);
my $t = NGCP::Panel::Utils::InvoiceTemplate::get_tt();
my $out = '';
my $pdf = '';
my $vars = {};
# TODO: index 170 seems the upper limit here, then the calllist breaks
$vars->{rescontact} = { $customer->contact->reseller->contract->contact->get_inflated_columns };
$vars->{customer} = { $customer->get_inflated_columns };
$vars->{custcontact} = { $customer->contact->get_inflated_columns };
$vars->{billprof} = { $billing_profile->get_inflated_columns };
prepare_contact_data($vars->{billprof});
prepare_contact_data($vars->{custcontact});
prepare_contact_data($vars->{rescontact});
$vars->{invoice} = {
period_start => $stime,
period_end => $etime,
serial => $serial,
amount_net => $invoice_data->{amount_net},
amount_vat => $invoice_data->{amount_vat},
amount_total => $invoice_data->{amount_total},
};
$vars->{calls} = $calllist,
$vars->{zones} = {
totalcost => $balance->cash_balance_interval,
data => [ values(%{ $zonecalls }) ],
};
$t->process(\$svg, $vars, \$out) || do {
my $error = $t->error();
my $error_msg = "error processing template, type=".$error->type.", info='".$error->info."'";
my $msg =$c->loc('Failed to render template. Type is [_1], info is [_2].', $error->type, $error->info);
die {
showdetails => $msg,
error => $error_msg,
httpcode => HTTP_UNPROCESSABLE_ENTITY,
};
};
NGCP::Panel::Utils::InvoiceTemplate::preprocess_svg(\$out);
NGCP::Panel::Utils::InvoiceTemplate::svg_pdf($c, \$out, \$pdf);
$invoice->update({
serial => $serial,
data => $pdf,
});
NGCP::Panel::Utils::Message::info(
c => $c,
cname => 'create',
log => $vars->{invoice},
desc => $c->loc('Invoice #[_1] successfully created', $invoice->id),
);
return $invoice;
}
sub check_invoice_data{
my($c,$params) = @_;
my($contract_id,$tmpl_id,$period,$period_start,$period_end) = @$params{qw/contract_id tmpl_id period period_start period_end/};
my $invoice_data = {};
my $schema = $c->model('DB');
my $customer_rs = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c, contract_id => $contract_id);
my $customer = $customer_rs->find({ 'me.id' => $contract_id });
unless($customer) {
die {
showdetails => $c->loc('Customer not found'),
error => "invalid contract_id $contract_id",
httpcode => HTTP_UNPROCESSABLE_ENTITY,
};
}
$invoice_data->{contract_id} = $contract_id;
my $tmpl = $schema->resultset('invoice_templates')->search({
id => $tmpl_id,
});
if($c->user->roles eq "admin") {
} elsif($c->user->roles eq "reseller") {
$tmpl = $tmpl->search({
reseller_id => $c->user->reseller_id,
});
}
$tmpl = $tmpl->first;
unless($tmpl) {
die {
showdetails => $c->loc('Invoice template not found'),
error => "invalid template id $tmpl_id",
httpcode => HTTP_UNPROCESSABLE_ENTITY,
};
}
unless($tmpl->data) {
die {
showdetails => $c->loc('Invoice template does not have an SVG stored yet'),
error => "invalid template id $tmpl_id, data is empty",
httpcode => HTTP_UNPROCESSABLE_ENTITY,
};
}
unless($customer->contact->reseller_id == $tmpl->reseller_id) {
die {
showdetails => $c->loc('Template and customer must belong to same reseller'),
error => "template id ".$tmpl->id." has different reseller than contract id $contract_id",
httpcode => HTTP_UNPROCESSABLE_ENTITY,
};
}
my $stime = $period_start ? NGCP::Panel::Utils::DateTime::from_string(
$period_start
) : NGCP::Panel::Utils::DateTime::from_string(
$period
)->truncate(to => 'month');
my $etime = $period_end ? NGCP::Panel::Utils::DateTime::from_string(
$period_end
) : $stime->clone->add(months => 1)->subtract(seconds => 1);
return($contract_id,$customer,$tmpl,$stime,$etime,$invoice_data);
}
1;
# vim: set tabstop=4 expandtab:

@ -150,6 +150,12 @@ sub error {
$msg = "$desc (@$error[0])";
$usr_text = "$desc (@$error[0])";
}
elsif (ref($error) eq "HASH" && $error->{showdetails})
{
my $error_msg = $error->{error} ? ' '.$error->{error} : '';
$msg = "$desc ($error->{showdetails}$error_msg)";
$usr_text = "$desc ($error->{showdetails})";
}
elsif (not $error->isa('DBIx::Class::Exception'))
{
$msg = "$desc ($error)";

Loading…
Cancel
Save