MT#53849 peer/reseller invoicing

Change-Id: I0d869edb697779e7d02caaea70c237f0c87f4d50
mr12.0
Rene Krenn 3 years ago
parent fd796a91ba
commit cde01e7d66

@ -82,7 +82,7 @@ sub create_item {
my $period = $form->values->{period};
my $item;
try {
my($contract_id,$customer,$tmpl,$stime,$etime,$invoice_data) = NGCP::Panel::Utils::Invoice::check_invoice_data($c, {
my ($contract,$tmpl,$stime,$etime,$invoice_data) = NGCP::Panel::Utils::Invoice::check_invoice_data($c, {
contract_id => $contract_id,
tmpl_id => $tmpl_id,
period_start => $period_start,
@ -90,8 +90,7 @@ sub create_item {
period => $period,
});
$item = NGCP::Panel::Utils::Invoice::create_invoice($c,{
contract_id => $contract_id,
customer => $customer,
contract => $contract,
stime => $stime,
etime => $etime,
tmpl => $tmpl,

@ -586,6 +586,48 @@ sub is_valid_noreseller_contact {
}
}
sub all_contracts_list :Chained('contract_list') :PathPart('all_contracts') :CaptureArgs(0) {
my ($self, $c) = @_;
my $now = NGCP::Panel::Utils::DateTime::current_local;
$c->stash->{contract_dt_columnsX} = NGCP::Panel::Utils::Datatables::set_columns($c, [
{ name => "id", search => 1, title => $c->loc("#") },
{ name => "external_id", search => 1, title => $c->loc("External #") },
{ name => "contact.email", search => 1, title => $c->loc("Contact Email") },
#{ name => 'billing_profile_name', accessor => "billing_profile_name", search => 0, title => $c->loc('Billing Profile'),
# literal_sql => NGCP::Panel::Utils::BillingMappings::get_actual_billing_mapping_stmt(c => $c, now => $now, projection => 'billing_profile.name' ) },
{ name => "status", search => 1, title => $c->loc("Status") },
{ name => "product.name", search => 0, title => $c->loc("Product") },
]);
my $rs_all_contracts = NGCP::Panel::Utils::Contract::get_contract_rs(
schema => $c->model('DB'),
now => $now,
include_terminated => 0,
);
$c->stash(rs_all_contracts => $rs_all_contracts);
#my @product_ids = map { $_->id; } $c->model('DB')->resultset('products')->search_rs({ 'class' => ['sippeering'] })->all;
#my $base_rs = $c->stash->{contract_select_rs};
#$c->stash->{peering_rs} = $base_rs->search({
# 'product_id' => { -in => [ @product_ids ] },
#});
$c->stash(ajax_uri => $c->uri_for_action("/contract/all_contracts_ajax"));
}
sub all_contracts_root :Chained('all_contracts_list') :PathPart('') :Args(0) {
}
sub all_contracts_ajax :Chained('all_contracts_list') :PathPart('ajax') :Args(0) {
my ($self, $c) = @_;
my $rs = $c->stash->{rs_all_contracts};
NGCP::Panel::Utils::Datatables::process($c, $rs, $c->stash->{contract_dt_columnsX});
$c->detach( $c->view("JSON") );
}
1;

@ -41,8 +41,9 @@ sub inv_list :Chained('/') :PathPart('invoice') :CaptureArgs(0) :Does(ACL) :ACLD
$c->stash->{inv_dt_columns} = NGCP::Panel::Utils::Datatables::set_columns($c, [
{ name => 'id', search => 1, title => $c->loc('#') },
{ name => 'contract.id', search => 1, title => $c->loc('Customer #') },
{ name => 'contract.contact.email', search => 1, title => $c->loc('Customer Email') },
{ name => 'contract.id', search => 1, title => $c->loc('Contract #') },
{ name => 'contract.contact.email', search => 1, title => $c->loc('Contract Email') },
{ name => 'contract.product.name', search => 1, title => $c->loc('Product #') },
{ name => 'serial', search => 1, title => $c->loc('Serial') },
]);
@ -169,9 +170,9 @@ sub create :Chained('inv_list') :PathPart('create') :Args() :Does(ACL) :ACLDetac
my $tmpl_id = $form->values->{template}{id};
delete $form->values->{template};
my $period = delete $form->values->{period};
my($customer,$tmpl,$stime,$etime,$invoice_data);
my($contract,$tmpl,$stime,$etime,$invoice_data);
($contract_id,$customer,$tmpl,$stime,$etime,$invoice_data) = NGCP::Panel::Utils::Invoice::check_invoice_data($c, {
($contract,$tmpl,$stime,$etime,$invoice_data) = NGCP::Panel::Utils::Invoice::check_invoice_data($c, {
contract_id => $contract_id,
tmpl_id => $tmpl_id,
period_start => undef,
@ -182,8 +183,7 @@ sub create :Chained('inv_list') :PathPart('create') :Args() :Does(ACL) :ACLDetac
$schema->set_transaction_isolation('READ COMMITTED');
$schema->txn_do(sub {
NGCP::Panel::Utils::Invoice::create_invoice($c,{
contract_id => $contract_id,
customer => $customer,
contract => $contract,
stime => $stime,
etime => $etime,
tmpl => $tmpl,

@ -33,6 +33,7 @@ sub template_list :Chained('/') :PathPart('invoicetemplate') :CaptureArgs(0) :Do
{ name => 'reseller.name', search => 1, title => $c->loc('Reseller') },
{ name => 'name', search => 1, title => $c->loc('Name') },
{ name => 'type', search => 1, title => $c->loc('Type') },
{ name => 'category', search => 1, title => $c->loc('Category') },
]);
$c->stash(template => 'invoice/template_list.tt');
@ -119,6 +120,7 @@ sub create :Chained('template_list_restricted') :PathPart('create') :Args() {
}
}
}
$form->process(
posted => $posted,
params => $c->request->params,
@ -142,6 +144,8 @@ sub create :Chained('template_list_restricted') :PathPart('create') :Args() {
$form->values->{reseller_id} = $c->user->reseller_id;
}
delete $form->values->{reseller};
$c->log->debug("roles: " . $c->user->roles);
my $dup_item = $schema->resultset('invoice_templates')->find({
reseller_id => $form->values->{reseller_id},
@ -152,7 +156,7 @@ sub create :Chained('template_list_restricted') :PathPart('create') :Args() {
}
my $tmpl_params = $form->values;
$tmpl_params->{data} //= NGCP::Panel::Utils::InvoiceTemplate::svg_content($c, $tmpl_params->{data});
$tmpl_params->{data} //= NGCP::Panel::Utils::InvoiceTemplate::svg_content($c, $tmpl_params->{category}, $tmpl_params->{data});
my $tmpl = $c->stash->{tmpl_rs}->create($tmpl_params);
delete $c->session->{created_objects}->{reseller};
@ -287,7 +291,7 @@ sub get_content_ajax :Chained('base') :PathPart('editcontent/get/ajax') :Args(0)
my ($self, $c) = @_;
my $tmpl = $c->stash->{tmpl};
my $content = NGCP::Panel::Utils::InvoiceTemplate::svg_content($c, $tmpl->data);
my $content = NGCP::Panel::Utils::InvoiceTemplate::svg_content($c, $tmpl->category, $tmpl->data);
$c->response->content_type('text/html');
$c->response->body($content);

@ -0,0 +1,20 @@
package NGCP::Panel::Field::AllContracts;
use HTML::FormHandler::Moose;
extends 'HTML::FormHandler::Field::Compound';
has_field 'id' => (
type => '+NGCP::Panel::Field::DataTable',
label => 'Contract',
do_label => 0,
do_wrapper => 0,
required => 1,
template => 'helpers/datatables_field.tt',
ajax_src => '/contract/all_contracts/ajax',
table_titles => ['#', 'Status', 'Contact Email', 'Product'],
table_fields => ['id', 'status', 'contact_email', 'product_name'],
);
no Moose;
1;
# vim: set tabstop=4 expandtab:

@ -10,8 +10,8 @@ has_field 'id' => (
required => 1,
ajax_src => '/invoicetemplate/ajax',
template => 'helpers/datatables_field.tt',
table_titles => ['#', 'Reseller', 'Name'],
table_fields => ['id', 'reseller_name', 'name'],
table_titles => ['#', 'Reseller', 'Name', 'Category'],
table_fields => ['id', 'reseller_name', 'name', 'category'],
);
no Moose;

@ -21,8 +21,8 @@ has_field 'template' => (
);
has_field 'contract' => (
type => '+NGCP::Panel::Field::CustomerContract',
label => 'Customer',
type => '+NGCP::Panel::Field::AllContracts',
label => 'Contract',
validate_when_empty => 1,
element_attr => {
rel => ['tooltip'],

@ -6,7 +6,6 @@ extends 'NGCP::Panel::Form::Invoice::TemplateReseller';
has_field 'reseller' => (
type => '+NGCP::Panel::Field::Reseller',
label => 'Reseller',
validate_when_empty => 1,
element_attr => {
rel => ['tooltip'],
title => ['The reseller id to assign this invoice template to.']
@ -16,9 +15,31 @@ has_field 'reseller' => (
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/reseller name type call_direction/],
render_list => [qw/reseller name type call_direction category/],
);
sub validate {
my ($self) = @_;
my $c = $self->ctx;
return unless $c;
my $category = $self->field('category')->value;
my $reseller_id = $self->field('reseller')->value;
$reseller_id = $reseller_id->{id} if $reseller_id;
if (($category eq 'customer'
or $category eq 'did')
and not $reseller_id) {
$self->field('reseller')->fields->[0]->add_error($c->loc("Reseller is required for category 'customer' or 'did'"));
} elsif (($category eq 'peer'
or $category eq 'reseller')
and $reseller_id) {
$self->field('reseller')->fields->[0]->add_error($c->loc("Reseller is must be empty for category 'peer' or 'reseller'"));
}
}
1;
# vim: set tabstop=4 expandtab:

@ -49,6 +49,23 @@ has_field 'call_direction' => (
},
);
has_field 'category' => (
type => 'Select',
label => 'Category',
required => 1,
options => [
{ label => 'customer', value => 'customer' },
{ label => 'peer', value => 'peer' },
{ label => 'reseller', value => 'reseller' },
{ label => 'did', value => 'did' },
],
default => 'customer',
element_attr => {
rel => ['tooltip'],
title => ['The category of the invoice.'],
},
);
has_field 'save' => (
type => 'Submit',
value => 'Save',
@ -59,7 +76,7 @@ has_field 'save' => (
has_block 'fields' => (
tag => 'div',
class => [qw/modal-body/],
render_list => [qw/name type call_direction/],
render_list => [qw/name type call_direction category/],
);
has_block 'actions' => (

@ -174,69 +174,112 @@ sub get_contract_zonesfees_rs {
my $contract_id = $params{contract_id};
my $subscriber_uuid = $params{subscriber_uuid};
my $group_detail = $params{group_by_detail};
my $zonecalls_rs_out = $c->model('DB')->resultset('cdr')->search( {
'call_status' => 'ok',
'source_user_id' => ($subscriber_uuid || { '!=' => '0' }),
my $category = $params{category};
my $q = {
call_status => 'ok',
start_time =>
[ -and =>
{ '>=' => $stime->epoch},
{ '<=' => $etime->epoch},
],
source_account_id => $contract_id,
},{
};
my $q_out = { %$q };
my $q_in = { %$q };
#my $q_intra = { %$q };
my $contract = $c->model('DB')->resultset('contracts')->find({ id => $contract_id });
my $class;
$class = $contract->product()->class() if $contract;
if ($class) {
if ($class eq 'sippeering' or $class eq 'pstnpeering') {
$category = 'carrier' unless $category;
$q_out->{source_provider_id} = $contract_id;
$q_out->{destination_provider_id} = { '!=' => $contract_id };
#$q_out->{source_user_id} = { '=' => '0' };
$q_in->{destination_provider_id} = $contract_id;
$q_in->{source_provider_id} = { '!=' => $contract_id };
#$q_in->{destination_user_id} = { '=' => '0' };
#$q_intra->{source_provider_id} = $contract_id;
#$q_intra->{destination_provider_id} = $contract_id;
} elsif ($class eq 'reseller') {
$category = 'reseller' unless $category;
$q_out->{source_provider_id} = $contract_id;
$q_out->{destination_provider_id} = { '!=' => $contract_id }; #no intra reseller calls
#$q_out->{source_user_id} = { '!=' => '0' };
$q_in->{destination_provider_id} = $contract_id;
$q_in->{source_provider_id} = { '!=' => $contract_id }; #no intra reseller calls
#$q_in->{destination_user_id} = { '!=' => '0' };
#$q_intra->{source_provider_id} = $contract_id;
#$q_intra->{destination_provider_id} = $contract_id;
} elsif ($class eq 'sipaccount' or $class eq 'pbxaccount') {
$category = 'customer' unless $category;
if ($subscriber_uuid) {
$q_out->{source_user_id} = $subscriber_uuid;
$q_out->{destination_account_id} = { '!=' => $contract_id };
$q_in->{destination_user_id} = $subscriber_uuid;
$q_in->{source_account_id} = { '!=' => $contract_id };
} else {
$q_out->{source_account_id} = $contract_id;
$q_out->{destination_account_id} = { '!=' => $contract_id }; #no intra contract calls
#$q_out->{source_user_id} = { '!=' => '0' };
$q_in->{destination_account_id} = $contract_id;
$q_in->{source_account_id} = { '!=' => $contract_id }; #no intra contract calls
#$q_in->{destination_user_id} = { '!=' => '0' };
#$q_intra->{source_account_id} = $contract_id;
#$q_intra->{destination_account_id} = $contract_id;
}
}
}
$category = 'carrier' if $category eq 'peer';
$category = 'customer' if $category eq 'did';
my $zonecalls_rs_out = $c->model('DB')->resultset('cdr')->search($q_out,{
'select' => [
{ sum => 'me.source_customer_cost', -as => 'customercost' },
{ sum => 'me.source_carrier_cost', -as => 'carriercost' },
{ sum => 'me.source_reseller_cost', -as => 'resellercost' },
{ sum => 'me.source_customer_free_time', -as => 'free_time' },
{ sum => "me.source_" . $category . "_free_time", -as => 'free_time' },
{ sum => 'me.duration', -as => 'duration' },
{ count => '*', -as => 'number' },
'source_customer_billing_zones_history.zone',
$group_detail ? 'source_customer_billing_zones_history.detail' : (),
"source_" . $category . "_billing_zones_history.zone",
$group_detail ? "source_" . $category . "_billing_zones_history.detail" : (),
],
'as' => [
qw/customercost carriercost resellercost free_time duration number zone/,
$group_detail ? 'zone_detail' : (),
],
join => 'source_customer_billing_zones_history',
join => "source_" . $category . "_billing_zones_history",
group_by => [
'source_customer_billing_zones_history.zone',
$group_detail ? 'source_customer_billing_zones_history.detail' : (),
"source_" . $category . "_billing_zones_history.zone",
$group_detail ? "source_" . $category . "_billing_zones_history.detail" : (),
],
order_by => 'source_customer_billing_zones_history.zone',
order_by => "source_" . $category . "_billing_zones_history.zone",
} );
my $zonecalls_rs_in = $c->model('DB')->resultset('cdr')->search( {
'call_status' => 'ok',
'destination_user_id' => ($subscriber_uuid || { '!=' => '0' }),
start_time =>
[ -and =>
{ '>=' => $stime->epoch},
{ '<=' => $etime->epoch},
],
destination_account_id => $contract_id,
},{
my $zonecalls_rs_in = $c->model('DB')->resultset('cdr')->search($q_in,{
'select' => [
{ sum => 'me.destination_customer_cost', -as => 'customercost' },
{ sum => 'me.destination_carrier_cost', -as => 'carriercost' },
{ sum => 'me.destination_reseller_cost', -as => 'resellercost' },
{ sum => 'me.destination_customer_free_time', -as => 'free_time' },
{ sum => "me.destination_" . $category . "_free_time", -as => 'free_time' },
{ sum => 'me.duration', -as => 'duration' },
{ count => '*', -as => 'number' },
'destination_customer_billing_zones_history.zone',
$group_detail ? 'destination_customer_billing_zones_history.detail' : (),
"destination_" . $category . "_billing_zones_history.zone",
$group_detail ? "destination_" . $category . "_billing_zones_history.detail" : (),
],
'as' => [
qw/customercost carriercost resellercost free_time duration number zone/,
$group_detail ? 'zone_detail' : (),
],
join => 'destination_customer_billing_zones_history',
join => "destination_" . $category . "_billing_zones_history",
group_by => [
'destination_customer_billing_zones_history.zone',
$group_detail ? 'destination_customer_billing_zones_history.detail' : (),
"destination_" . $category . "_billing_zones_history.zone",
$group_detail ? "destination_" . $category . "_billing_zones_history.detail" : (),
],
order_by => 'destination_customer_billing_zones_history.zone',
order_by => "destination_" . $category . "_billing_zones_history.zone",
} );
return ($zonecalls_rs_in, $zonecalls_rs_out);
@ -285,32 +328,60 @@ sub get_contract_zonesfees {
return \%allzones;
}
sub get_contract_calls_rs{
sub get_contract_calls_rs {
my %params = @_;
(my($c,$customer_contract_id,$stime,$etime,$call_direction)) = @params{qw/c customer_contract_id stime etime call_direction/};
my($c,$contract_id,$stime,$etime,$call_direction,$category) = @params{qw/c contract_id stime etime call_direction category/};
$stime ||= NGCP::Panel::Utils::DateTime::current_local()->truncate( to => 'month' );
$etime ||= $stime->clone->add( months => 1 );
my @cols = ();
push(@cols,qw/source_user source_domain source_cli destination_user_in/);
#push(@cols,NGCP::Panel::Utils::CallList::get_suppression_id_colnames());
push(@cols,qw/start_time duration call_type source_customer_cost/);
my @colnames = @cols;
push(@cols,qw/source_customer_billing_zones_history.zone source_customer_billing_zones_history.detail/);
push(@colnames,qw/zone zone_detail/);
my $calls_rs = $c->model('DB')->resultset('cdr')->search({
my $q = {
'call_status' => 'ok',
'start_time' =>
[ -and =>
{ '>=' => $stime->epoch},
{ '<=' => $etime->epoch},
],
},{
};
my $contract = $c->model('DB')->resultset('contracts')->find({ id => $contract_id });
my $class;
$class = $contract->product()->class() if $contract;
my $contract_type;
if ($class) {
if ($class eq 'sippeering' or $class eq 'pstnpeering') {
$category = 'carrier' unless $category;
$contract_type = 'provider';
#if ($call_direction eq 'in') {
# $call_direction = 'out';
#} elsif ($call_direction eq 'out') {
# $call_direction = 'in';
#}
} elsif ($class eq 'reseller') {
$category = 'reseller' unless $category;
$contract_type = 'provider';
} elsif ($class eq 'sipaccount' or $class eq 'pbxaccount') {
$category = 'customer' unless $category;
$contract_type = 'account';
}
}
$category = 'carrier' if $category eq 'peer';
$category = 'customer' if $category eq 'did';
my @cols = ();
push(@cols,qw/source_user source_domain source_cli destination_user_in/);
#push(@cols,NGCP::Panel::Utils::CallList::get_suppression_id_colnames());
push(@cols,qw/start_time duration call_type/);
push(@cols,'source_' . $category . '_cost');
my @colnames = @cols;
push(@cols,'source_' . $category . '_billing_zones_history.zone');
push(@cols,'source_' . $category . '_billing_zones_history.detail');
push(@colnames,qw/zone zone_detail/);
my $calls_rs = $c->model('DB')->resultset('cdr')->search($q,{
select => \@cols,
as => \@colnames,
'join' => 'source_customer_billing_zones_history',
'join' => 'source_' . $category . '_billing_zones_history',
'order_by' => 'start_time',
}
);
@ -318,21 +389,21 @@ sub get_contract_calls_rs{
if ($call_direction) {
if ($call_direction eq "in") {
$calls_rs = $calls_rs->search({
destination_account_id => $customer_contract_id,
'destination_' . $contract_type . '_id' => $contract_id,
});
#suppression rs decoration at last, after any "select =>"
return NGCP::Panel::Utils::CallList::call_list_suppressions_rs($c,$calls_rs,NGCP::Panel::Utils::CallList::SUPPRESS_IN);
} elsif ($call_direction eq "out") {
$calls_rs = $calls_rs->search({
source_account_id => $customer_contract_id,
'source_' . $contract_type . '_id' => $contract_id,
});
#suppression rs decoration at last, after any "select =>"
return NGCP::Panel::Utils::CallList::call_list_suppressions_rs($c,$calls_rs,NGCP::Panel::Utils::CallList::SUPPRESS_OUT);
} elsif ($call_direction eq "in_out") {
$calls_rs = $calls_rs->search({
-or => [
{ source_account_id => $customer_contract_id },
{ destination_account_id => $customer_contract_id },
{ 'source_' . $contract_type . '_id' => $contract_id },
{ 'destination_' . $contract_type . '_id' => $contract_id },
],
});
#suppression rs decoration at last, after any "select =>"

@ -11,15 +11,23 @@ use HTML::Entities;
use Geography::Countries qw/country/;
use HTTP::Status qw(:constants);
sub get_invoice_amounts{
sub get_invoice_amounts {
my(%params) = @_;
my($customer_contract,$billing_profile,$contract_balance,$zonecalls) = @params{qw/customer_contract billing_profile contract_balance zonecalls/};
my($customer_contract,$billing_profile,$contract_balance,$zonecalls,$category) = @params{qw/customer_contract billing_profile contract_balance zonecalls category/};
my $invoice = {};
$billing_profile->{interval_charge} //= 0.0;
$customer_contract->{vat_rate} //= 0.0;
if ($zonecalls) {
$invoice->{amount_net} = 0.0;
map { $invoice->{amount_net} += $_->{customercost}; } values %$zonecalls;
map {
if ($category eq 'customer' or $category eq 'did') {
$invoice->{amount_net} += $_->{customercost};
} elsif ($category eq 'reseller') {
$invoice->{amount_net} += $_->{resellercost};
} elsif ($category eq 'peer') {
$invoice->{amount_net} += $_->{carriercost};
}
} values %$zonecalls;
} else {
$contract_balance->{cash_balance_interval} //= 0.0;
#use Data::Dumper;
@ -51,9 +59,9 @@ sub prepare_contact_data{
#return $contact;
}
sub create_invoice{
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($contract,$stime,$etime,$tmpl,$invoice_data) = @$params{qw/contract stime etime tmpl invoice_data/};
my $invoice;
@ -62,64 +70,87 @@ sub create_invoice{
#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,
contract => $contract,
stime => $stime,
etime => $etime,);
$stime = $balance->start;
$etime = $balance->end;
my $bm_actual = NGCP::Panel::Utils::BillingMappings::get_actual_billing_mapping(
c => $c,
contract => $customer,
contract => $contract,
now => $balance->start);
my $billing_profile = $bm_actual->billing_profile;
my $zonecalls = NGCP::Panel::Utils::Contract::get_contract_zonesfees(
my $zonecalls = {};
my $did_zonecalls = [];
if ($tmpl->category eq 'did') {
foreach my $subs ($schema->resultset('voip_subscribers')->search({
contract_id => $contract->id,
#status => { '!=' => 'terminated' },
#'provisioning_voip_subscriber.is_pbx_group' => 0,
}, #{
#join => 'provisioning_voip_subscriber',
#}
)->all) {
my $zc = NGCP::Panel::Utils::Contract::get_contract_zonesfees(
c => $c,
contract_id => $contract->id,
stime => $stime,
etime => $etime,
call_direction => $tmpl->call_direction,
group_by_detail => 1,
category => $tmpl->category,
subscriber_uuid => $subs->uuid,
);
my $s = { $subs->get_inflated_columns };
$s->{primary_number} = { $subs->primary_number->get_inflated_columns } if $subs->primary_number;
$s->{prov_subscriber} = { $subs->provisioning_voip_subscriber->get_inflated_columns } if $subs->provisioning_voip_subscriber;
push(@$did_zonecalls,{
subscriber => $s,
zonecalls => $zc,
totalcost => 0.0,
totalduration => 0.0,
}) if scalar keys %$zc;
}
}
$zonecalls = NGCP::Panel::Utils::Contract::get_contract_zonesfees(
c => $c,
contract_id => $contract_id,
contract_id => $contract->id,
stime => $stime,
etime => $etime,
call_direction => $tmpl->call_direction,
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,
call_direction => $tmpl->call_direction,
);
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;
#}
category => $tmpl->category,
);
my $calllist = [];
if ($tmpl->category eq 'customer') {
my $calllist_rs = NGCP::Panel::Utils::Contract::get_contract_calls_rs(
c => $c,
contract_id => $contract->id,
stime => $stime,
etime => $etime,
call_direction => $tmpl->call_direction,
category => $tmpl->category,
);
$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
$call->{source_reseller_cost} += 0.0 if exists $call->{source_reseller_cost};
$call->{source_carrier_cost} += 0.0 if exists $call->{source_carrier_cost};
NGCP::Panel::Utils::CallList::suppress_cdr_fields($c,$call,$_);
} $calllist_rs->all ];
}
my $invoice_amounts = get_invoice_amounts(
customer_contract => {$customer->get_inflated_columns},
customer_contract => {$contract->get_inflated_columns}, #support legacy
contract => {$contract->get_inflated_columns},
billing_profile => {$billing_profile->get_inflated_columns},
contract_balance => {$balance->get_inflated_columns},
zonecalls => $zonecalls,
category => $tmpl->category,
);
@{$invoice_data}{qw/amount_net amount_vat amount_total/} = @$invoice_amounts{qw/amount_net amount_vat amount_total/};
@ -156,13 +187,16 @@ sub create_invoice{
# 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->{rescontact} = { $contract->contact->reseller->contract->contact->get_inflated_columns } if $contract->contact->reseller;
$vars->{customer} = { $contract->get_inflated_columns };
$vars->{contract} = { $contract->get_inflated_columns };
$vars->{custcontact} = { $contract->contact->get_inflated_columns };
$vars->{contact} = { $contract->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->{contact});
prepare_contact_data($vars->{rescontact});
$vars->{invoice} = {
@ -173,6 +207,7 @@ sub create_invoice{
amount_vat => $invoice_data->{amount_vat},
amount_total => $invoice_data->{amount_total},
contract_balance => { $balance->get_inflated_columns },
call_direction => ($tmpl->call_direction eq 'in' ? 'from' : ($tmpl->call_direction eq 'out' ? 'to' : ($tmpl->call_direction eq 'in_out' ? 'from/to' : ''))),
};
$vars->{calls} = $calllist;
$vars->{zones} = {
@ -181,9 +216,35 @@ sub create_invoice{
data => [ values(%{ $zonecalls }) ],
};
map {
$vars->{zones}->{totalcost} += $_->{customercost};
if ($tmpl->category eq 'customer' or $tmpl->category eq 'did') {
$vars->{zones}->{totalcost} += $_->{customercost};
} elsif ($tmpl->category eq 'reseller') {
$vars->{zones}->{totalcost} += $_->{resellercost};
} elsif ($tmpl->category eq 'peer') {
$vars->{zones}->{totalcost} += $_->{carriercost};
}
$vars->{zones}->{totalduration} += $_->{duration};
} values %$zonecalls;
map {
my $did_zc = $_;
map {
if ($tmpl->category eq 'customer' or $tmpl->category eq 'did') {
$did_zc->{totalcost} += $_->{customercost};
} elsif ($tmpl->category eq 'reseller') {
$did_zc->{totalcost} += $_->{resellercost};
} elsif ($tmpl->category eq 'peer') {
$did_zc->{totalcost} += $_->{carriercost};
}
$did_zc->{totalduration} += $_->{duration};
$did_zc->{data} = [ values(%{ delete $did_zc->{zonecalls} }) ];
} values %{$did_zc->{zonecalls}};
} @$did_zonecalls;
$vars->{did_zones} = $did_zonecalls;
#use Data::Dumper;
#$c->log->debug(Dumper($did_zonecalls));
$t->process(\$svg, $vars, \$out) || do {
my $error = $t->error();
my $error_msg = "error processing template, type=".$error->type.", info='".$error->info."'";
@ -213,56 +274,80 @@ sub create_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 ($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 = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c)->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") {
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) {
unless ($tmpl) {
die {
showdetails => $c->loc('Invoice template not found'),
error => "invalid template id $tmpl_id",
httpcode => HTTP_UNPROCESSABLE_ENTITY,
};
}
unless($tmpl->data) {
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,
};
}
my $contract;
if ('customer' eq $tmpl->category or 'did' eq $tmpl->category) {
$contract = NGCP::Panel::Utils::Contract::get_customer_rs(c => $c)->find({ 'me.id' => $contract_id });
unless($contract) {
die {
showdetails => $c->loc('Customer not found'),
error => "invalid contract_id $contract_id",
httpcode => HTTP_UNPROCESSABLE_ENTITY,
};
}
unless($contract->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,
};
}
} else {
my @product_ids = map { $_->id; } $schema->resultset('products')->search_rs({ 'class' => ['pstnpeering','sippeering','reseller'] })->all;
$contract = NGCP::Panel::Utils::Contract::get_contract_rs(c => $c)->search_rs({
'me.id' => $contract_id,
'product_id' => { -in => [ @product_ids ] },
},{
join => 'contact',
})->first;
unless($contract) {
die {
showdetails => $c->loc('Contract not found'),
error => "invalid contract_id $contract_id",
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,
};
}
$invoice_data->{contract_id} = $contract_id;
#$invoice_data->{category} = $tmpl->category;
my $stime = $period_start ? NGCP::Panel::Utils::DateTime::from_string(
$period_start
@ -273,7 +358,8 @@ sub check_invoice_data{
$period_end
) : $stime->clone->add(months => 1)->subtract(seconds => 1);
return($contract_id,$customer,$tmpl,$stime,$etime,$invoice_data);
return ($contract,$tmpl,$stime,$etime,$invoice_data);
}
1;
# vim: set tabstop=4 expandtab:
1;

@ -159,18 +159,20 @@ sub get_tt {
}
sub svg_content{
my ($c, $content) = @_;
my ($c, $category, $content) = @_;
unless ($content) {
#default is the same for all - I would like to move it as something constant to itils
my $default = 'invoice/default/invoice_template_svg.tt';
my $default = 'invoice/default/' . $category . '_invoice_template_svg.tt';
my $t = NGCP::Panel::Utils::InvoiceTemplate::get_tt();
try {
$content = $t->context->insert($default);
} catch($e) {
# TODO: handle error!
$c and $c->log->error("failed to load default invoice template: $e");
my $msg = "failed to load default $category invoice template: $e";
$c and $c->log->error($msg);
die($msg);
return;
}
}

@ -6,11 +6,11 @@
# money_signs = 3;
PROCESS "invoice/default/invoice_template_aux.tt";
money_format(amount=(billprof.interval_charge * 100), comma='.'); fixfee = aux.val;
money_format(amount=(billprof.interval_charge), comma='.'); fixfee = aux.val;
money_format(amount=(zones.totalcost), comma='.'); zonefee = aux.val;
money_format(amount=(invoice.amount_net * 100), comma='.'); netfee = aux.val;
money_format(amount=(invoice.amount_vat * 100), comma='.'); vatfee = aux.val;
money_format(amount=(invoice.amount_total * 100), comma='.'); allfee = aux.val;
money_format(amount=(invoice.amount_net), comma='.'); netfee = aux.val;
money_format(amount=(invoice.amount_vat), comma='.'); vatfee = aux.val;
money_format(amount=(invoice.amount_total), comma='.'); allfee = aux.val;
cur = billprof.currency;
p_start = date_format(thedate=invoice.period_start, format='%Y-%m-%d');
p_end = date_format(thedate=invoice.period_end, format='%Y-%m-%d');
@ -25,7 +25,7 @@
<text x="56.7" y="765.45">[% rescontact.company %]</text>
<text x="56.7" y="779.625">[% rescontact.street %]</text>
<text x="56.7" y="793.8">[% rescontact.postcode %] [% custcontact.city %]</text>
<text x="56.7" y="793.8">[% rescontact.postcode %] [% rescontact.city %]</text>
<text x="56.7" y="807.975">[% rescontact.country %]</text>
<text x="538.65" y="765.45" text-anchor="end">Company Reg.Nr.: [% rescontact.comregnum %]</text>
@ -82,7 +82,7 @@
<text x="56.7" y="467.775" font-family="Verdana" font-size="8" font-weight="bold">Call Summary</text>
<text x="56.7" y="467.775" font-family="Verdana" font-size="8" font-weight="bold">Calls [% invoice.call_direction %] other Customers and Subscribers</text>
<line x1="62.37" y1="473.445" x2="538.65" y2="473.445" style="stroke:#000000;stroke-width:1;" />
<text x="62.37" y="484.785" font-family="Verdana" font-size="8" font-weight="bold">Zone</text>
<text text-anchor="end" x="283.5" y="484.785" font-family="Verdana" font-size="8" font-weight="bold">Quantity</text>

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

@ -67,6 +67,7 @@
END;
aux.lasty = y;
END;
MACRO apply_units(item) BLOCK;
IF server_process_units == 'none';
'';

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

Loading…
Cancel
Save