Merge branch 'master' of git.freeside.biz:/home/git/freeside
authorMark Wells <mark@freeside.biz>
Sat, 12 Jan 2013 20:08:42 +0000 (12:08 -0800)
committerMark Wells <mark@freeside.biz>
Sat, 12 Jan 2013 20:08:42 +0000 (12:08 -0800)
FS/FS/Schema.pm
FS/FS/cdr/gsm_tap3_12.pm
FS/FS/svc_phone.pm
httemplate/edit/svc_phone.cgi
httemplate/view/svc_phone.cgi

index 5ac2b5f..bde0220 100644 (file)
@@ -3600,6 +3600,7 @@ sub tables_hashref {
         'svcnum',       'int',         '',      '', '', '', 
         'countrycode',  'varchar',     '',       3, '', '', 
         'phonenum',     'varchar',     '',      15, '', '',  #12 ?
+        'sim_imsi',     'varchar', 'NULL',      15, '', '',
         'pin',          'varchar', 'NULL', $char_d, '', '',
         'sip_password', 'varchar', 'NULL', $char_d, '', '',
         'phone_name',   'varchar', 'NULL', $char_d, '', '',
index d1536c0..8f50690 100644 (file)
@@ -4,7 +4,7 @@ use base qw( FS::cdr );
 use strict;
 use vars qw( %info );
 use Time::Local;
-#use Data::Dumper;
+use Data::Dumper;
 
 %info = (
   'name'          => 'GSM TAP3 release 12',
@@ -29,7 +29,7 @@ use Time::Local;
                     'src'                => sub { shift->{mobileOriginatedCall}{basicCallInformation}{chargeableSubscriber}{simChargeableSubscriber}{msisdn} },
                     'charged_party_imsi' => sub { shift->{mobileOriginatedCall}{basicCallInformation}{chargeableSubscriber}{simChargeableSubscriber}{imsi} },
                     'dst'                => sub { shift->{mobileOriginatedCall}{basicCallInformation}{destination}{calledNumber} }, #dialledDigits?
-                    'carrierid'          => sub { shift->{mobileOriginatedCall}{locationInformation}{networkLocation}{recEntityCode} },
+                    'carrierid'          => sub { shift->{mobileOriginatedCall}{locationInformation}{networkLocation}{recEntityCode} }, #XXX translate to recEntityId via info in header
                     'userfield'          => sub { shift->{mobileOriginatedCall}{operatorSpecInformation}[0] },
                     'servicecode'        => sub { shift->{mobileOriginatedCall}{basicServiceUsedList}[0]{basicService}{serviceCode}{teleServiceCode} },
                     'upstream_price'     => sub { sprintf('%.5f', shift->{mobileOriginatedCall}{basicServiceUsedList}[0]{chargeInformationList}[0]{chargeDetailList}[0]{charge} / 100000 ) }, #XXX numberOfDecimalPlaces in header
@@ -40,6 +40,429 @@ use Time::Local;
   },
 );
 
+#accepts qsearch parameters as a hash or list of name/value pairs, but not
+#old-style qsearch('cdr', { field=>'value' })
+
+use Date::Format;
+sub tap3_12_export {
+  my %qsearch = ();
+  if ( ref($_[0]) eq 'HASH' ) {
+    %qsearch = %{ $_[0] };
+  } else {
+    %qsearch = @_;
+  }
+
+  #if these get huge we might need to get a count and do a paged search
+  my @cdrs = qsearch({ 'table'=>'cdr', %qsearch, 'order_by'=>'calldate ASC' });
+
+  eval "use Convert::ASN1";
+  die $@ if $@;
+
+  my $asn = Convert::ASN1->new;
+  $asn->prepare( _asn_spec() ) or die $asn->error;
+
+  my $TransferBatch = $asn->find('TransferBatch') or die $asn->error;
+
+  my %hash = _TransferBatch(); #static information etc.
+
+  my $utcTimeOffset = '+0300'; #XXX local timezone at least
+
+  my $now = time;
+
+  ###
+  # accountingInfo
+  ###
+
+  ###
+  # batchControlInfo
+  ###
+
+  #optional
+  $hash{batchControlInfo}->{fileCreationTimeStamp}   = { 'localTimeStamp' => time2str('%Y%m%d%H%M%S', $now),
+                                                         'utcTimeOffset'  => $utcTimeOffset,
+                                                       };
+  #XXX what do these do?  do they need to be different from fileCreationTimeStamp?
+  $hash{batchControlInfo}->{transferCutOffTimeStamp} = { 'localTimeStamp' => time2str('%Y%m%d%H%M%S', $now),
+                                                         'utcTimeOffset'  => $utcTimeOffset,
+                                                       };
+
+  $hash{batchControlInfo}->{fileAvailableTimeStamp}  = { 'localTimeStamp' => time2str('%Y%m%d%H%M%S', $now),
+                                                          'utcTimeOffset'  => $utcTimeOffset,
+                                                        };
+
+  #XXX
+  $hash{batchControlInfo}->{sender} = 'MDGTM';
+  $hash{batchControlInfo}->{recipient} = 'GNQHT';
+  $hash{batchControlInfo}->{fileSequenceNumber} = '00178'; #XXX global?  per recipient?
+
+  ###
+  # networkInfo
+  ###
+
+  $hash{networkInfo}->{utcTimeOffsetInfo}[0]{utcTimeOffset} = $utcTimeOffset;
+
+  #XXX recording entity IDs, referenced by recEntityCode
+  #$hash->{networkInfo}->{recEntityInfo}[0]{recEntityId} = '340010100';
+  #$hash->{networkInfo}->{recEntityInfo}[1]{recEntityId} = '240556000000';
+
+  ###
+  # auditControlInfo
+  ###
+
+  #mandatory
+  $hash{auditControlInfo}->{callEventDetailsCount} = scalar(@cdrs);
+
+  #these two are optional
+  $hash{auditControlInfo}->{earliestCallTimeStamp} = { 'localTimeStamp' => time2str('%Y%m%d%H%M%S', $cdrs[0]->calldate_unix),
+                                                       'utcTimeOffset'  => $utcTimeOffset,
+                                                     };
+  $hash{auditControlInfo}->{latestCallTimeStamp}   = { 'localTimeStamp' => time2str('%Y%m%d%H%M%S', $cdrs[-1]->calldate_unix),
+                                                       'utcTimeOffset'  => $utcTimeOffset,
+                                                     };
+
+  #mandatory
+  my $totalCharge = 0;
+  $totalCharge += $_->rated_price foreach @cdrs;
+  $hash{totalCharge} = sprintf('%.5f', $totalCharge);
+
+  ###
+  # callEventDetails
+  ###
+
+  #one of Mobile Originated Call, Mobile Terminated Call, Mobile Session, Messaging Event, Supplementary Service Event, Service Centre Usage, GPRS Call, Content Transaction or Location Service
+  # Each occurrence must have no more than one of these present
+
+  $hash{callEventDetails} = [
+    map {
+          { #either tele or bearer service usage originated by the mobile subscription (others?)
+            'mobileOriginatedCall' => {
+
+              #identifies the Network Location, which includes the MSC responsible for handling the call and, where appropriate, the Geographical Location of the mobile
+              'locationInformation' => {
+                                         'networkLocation' => {
+                                                                'recEntityCode' => $_->carrierid, #XXX Recording Entity (per 2.5, from "Reference Tables")
+                                                              }
+                                       },
+
+              #Operator Specific Information: beyond the scope of TAP and has been bilaterally agreed
+              'operatorSpecInformation' => [
+                                             $_->userfield, ##'|Seq: 178 Loc: 1|'
+                                           ],
+
+              #The type of service used together with all related charging information
+              'basicServiceUsedList' => [
+                                          {
+                                            #identifies the actual Basic Service used
+                                            'basicService' => {
+                                                                #one of Teleservice Code or Bearer Service Code as determined by the service type used
+                                                                'serviceCode' => {
+                                                                                   #XXX
+                                                                                   #00 All teleservices
+                                                                                   #10 All Speech transmission services
+                                                                                   #11 Telephony
+                                                                                   #12 Emergency calls
+                                                                                   #20 All SMS Services
+                                                                                   #21 Short Message MT/PP
+                                                                                   #22 Short Message MO/PP
+                                                                                   #60 All Fax Services
+                                                                                   #61 Facsimile Group 3 & alternative speech
+                                                                                   #62 Automatic Facsimile Group 3
+                                                                                   #63 Automatic Facsimile Group 4
+                                                                                   #70 All data teleservices (compound)
+                                                                                   #80 All teleservices except SMS (compound)
+                                                                                   #90 All voice group call services
+                                                                                   #91 Voice group call
+                                                                                   #92 Voice broadcast call
+                                                                                   'teleServiceCode' => $_->servicecode, #'11'
+
+                                                                                   #Bearer Service Code
+                                                                                   # Must be present within group Service Code where the type of service used
+                                                                                   #  was a bearer service. Must not be present when the type of service used
+                                                                                   #  was a tele service and, therefore, Teleservice Code is present.
+                                                                                   # Group Bearer Codes, identifiable by the description ‘All’, should only
+                                                                                   #  be used where details of the specific services affected are not
+                                                                                   #  available from the network.
+                                                                                   #00 All Bearer Services
+                                                                                   #20 All Data Circuit Asynchronous Services
+                                                                                   #21 Duplex Asynch. 300bps data circuit
+                                                                                   #22 Duplex Asynch. 1200bps data circuit
+                                                                                   #23 Duplex Asynch. 1200/75bps data circuit
+                                                                                   #24 Duplex Asynch. 2400bps data circuit
+                                                                                   #25 Duplex Asynch. 4800bps data circuit
+                                                                                   #26 Duplex Asynch. 9600bps data circuit
+                                                                                   #27 General Data Circuit Asynchronous Service
+                                                                                   #30 All Data Circuit Synchronous Services
+                                                                                   #32 Duplex Synch. 1200bps data circuit
+                                                                                   #34 Duplex Synch. 2400bps data circuit
+                                                                                   #35 Duplex Synch. 4800bps data circuit
+                                                                                   #36 Duplex Synch. 9600bps data circuit
+                                                                                   #37 General Data Circuit Synchronous Service
+                                                                                   #40 All Dedicated PAD Access Services
+                                                                                   #41 Duplex Asynch. 300bps PAD access
+                                                                                   #42 Duplex Asynch. 1200bps PAD access
+                                                                                   #43 Duplex Asynch. 1200/75bps PAD access
+                                                                                   #44 Duplex Asynch. 2400bps PAD access
+                                                                                   #45 Duplex Asynch. 4800bps PAD access
+                                                                                   #46 Duplex Asynch. 9600bps PAD access
+                                                                                   #47 General PAD Access Service
+                                                                                   #50 All Dedicated Packet Access Services
+                                                                                   #54 Duplex Synch. 2400bps PAD access
+                                                                                   #55 Duplex Synch. 4800bps PAD access
+                                                                                   #56 Duplex Synch. 9600bps PAD access
+                                                                                   #57 General Packet Access Service
+                                                                                   #60 All Alternat Speech/Asynchronous Services
+                                                                                   #70 All Alternate Speech/Synchronous Services
+                                                                                   #80 All Speech followed by Data Asynchronous Services
+                                                                                   #90 All Speech followed by Data Synchronous Services
+                                                                                   #A0 All Data Circuit Asynchronous Services (compound)
+                                                                                   #B0 All Data Circuit Synchronous Services (compound)
+                                                                                   #C0 All Asynchronous Services (compound)
+                                                                                 }
+                                                                #conditionally also contain the following for UMTS: Transparency Indicator, Fixed Network User
+                                                                # Rate, User Protocol Indicator, Guaranteed Bit Rate and Maximum Bit Rate
+                                                              },
+
+                                            #Charge information is provided for all chargeable elements except within Messaging Event and Mobile Session call events
+                                            # must contain Charged Item and at least one occurrence of Charge Detail
+                                            'chargeInformationList' => [
+                                                                         {
+                                                                           #XXX
+                                                                           #mandatory
+                                                                           # the charging principle applied and the unitisation of Chargeable Units.  It
+                                                                           #  is not intended to identify the service used.
+                                                                           #A: Call set up attempt
+                                                                           #C: Content
+                                                                           #D: Duration based charge
+                                                                           #E: Event based charge
+                                                                           #F: Fixed (one-off) charge
+                                                                           #L: Calendar (for example daily usage charge)
+                                                                           #V: Volume (outgoing) based charge
+                                                                           #W: Volume (incoming) based charge
+                                                                           #X: Volume (total volume) based charge
+                                                                           #(?? fields to be used as a basis for the calculation of the correct Charge
+                                                                           #  A: Chargeable Units (if present)
+                                                                           #  D,V,W,X: Chargeable Units
+                                                                           #  C: Depends on the content
+                                                                           #  E: Not Applicable
+                                                                           #  F: Not Applicable
+                                                                           #  L: Call Event Start Timestamp)
+                                                                           'chargedItem' => 'D',
+
+                                                                           # the IOT used by the VPMN to price the call
+                                                                           'callTypeGroup' => {
+
+                                                                                                #The highest category call type in respect of the destination of the call
+                                                                                                #0: Unknown/Not Applicable
+                                                                                                #1: National
+                                                                                                #2: International
+                                                                                                #10: HGGSN/HP-GW
+                                                                                                #11: VGGSN/VP-GW
+                                                                                                #12: Other GGSN/Other P-GW
+                                                                                                #100: WLAN
+                                                                                                'callTypeLevel1' => $_->calltypenum,
+
+                                                                                                #the sub category of Call Type Level 1
+                                                                                                #0: Unknown/Not Applicable
+                                                                                                #1: Mobile
+                                                                                                #2: PSTN
+                                                                                                #3: Non Geographic
+                                                                                                #4: Premium Rate
+                                                                                                #5: Satellite destination
+                                                                                                #6: Forwarded call
+                                                                                                #7: Non forwarded call
+                                                                                                #10: Broadband
+                                                                                                #11: Narrowband
+                                                                                                #12: Conversational
+                                                                                                #13: Streaming
+                                                                                                #14: Interactive
+                                                                                                #15: Background
+                                                                                                'callTypeLevel2' => 0,
+
+                                                                                                #the sub category of Call Type Level 2
+                                                                                                'callTypeLevel3' => 0,
+                                                                                              },
+
+                                                                           #mandatory, at least one occurence must be present
+                                                                           #A repeating group detailing the Charge and/or charge element
+                                                                           # Note that, where a Charge has been levied, even where that Charge is zero,
+                                                                           #  there must be one occurance, and only one, with a Charge Type of '00'
+                                                                           'chargeDetailList' => [
+                                                                                                   {
+                                                                                                     #mandatory
+                                                                                                     # after discounts have been deducted but before any tax is added
+                                                                                                     'charge'          => $_->rated_price * 100000, #XXX numberOfDecimalPlaces 
+
+                                                                                                     #mandatory
+                                                                                                     # the type of charge represented
+                                                                                                     #00: Total charge for Charge Information (the invoiceable value)
+                                                                                                     #01: Airtime charge
+                                                                                                     #02: reserved
+                                                                                                     #03: Toll charge
+                                                                                                     #04: Directory assistance
+                                                                                                     #05–20: reserved
+                                                                                                     #21: VPMN surcharge
+                                                                                                     #50: Total charge for Charge Information according to the published IOT
+                                                                                                     #  Note that the use of value 50 is only for use by bilateral agreement, use without
+                                                                                                     #   bilateral agreement can be treated as per reserved values, that is ‘out of range’
+                                                                                                     #69–99: reserved
+                                                                                                     'chargeType'      => '00',
+
+                                                                                                     #conditional
+                                                                                                     # the number of units which are chargeable within the Charge Detail, this may not
+                                                                                                     # correspond to the number of rounded units charged.
+                                                                                                     # The item Charged Item defines what the units represent.
+                                                                                                     'chargeableUnits' => $_->quantity_able,
+
+                                                                                                     #optional
+                                                                                                     # the rounded number of units which are actually charged for
+                                                                                                     'chargedUnits'    => $_->quantity,
+                                                                                                   }
+                                                                                                 ],
+                                                                           'exchangeRateCode' => 1, #from header
+                                                                         }
+                                                                       ]
+                                          }
+                                        ],
+
+              #MO Basic Call Information provides the basic detail of who made the call and where to in respect of mobile originated traffic.
+              'basicCallInformation' => {
+                                          #mandatory
+                                          # the identification of the chargeable subscriber.
+                                          #  The group must contain either the IMSI or the MIN of the Chargeable Subscriber, but not both.
+                                          'chargeableSubscriber' => {
+                                                                      'simChargeableSubscriber' => {
+                                                                                                     'msisdn' => $_->charged_party, #src
+                                                                                                     'imsi'   => $_->charged_party_imsi,
+                                                                                                   }
+                                                                    },
+                                          # the start of the call event
+                                          'callEventStartTimeStamp' => {
+                                                                         'localTimeStamp' => time2str('%Y%m%d%H%M%S', $_->startdate),
+                                                                         'utcTimeOffsetCode' => 1
+                                                                       },
+
+                                          # the actual total duration of a call event as a number of seconds
+                                          'totalCallEventDuration' => $_->duration,
+
+                                          #conditional
+                                          # the number dialled by the subscriber (Called Number)
+                                          #  or the SMSC Address in case of SMS usage or in cases involving supplementary services
+                                          #   such as call forwarding or transfer etc., the number to which the call is routed
+                                          'destination' => {
+                                                             #the international representation of the destination
+                                                             'calledNumber' => $_->dst,
+
+                                                             #the actual digits as dialled by the subscriber, i.e. unmodified, in establishing a call
+                                                             # This will contain ‘+’ and ‘#’ where appropriate.
+                                                             #'dialledDigits' => '322221350'
+                                                           },
+                                        }
+            }
+          };
+        }
+      @cdrs
+  ];
+
+
+  ###
+
+
+  my $pdu = $TransferBatch->encode( \%hash );
+
+  return $pdu;
+
+}
+
+sub _TransferBatch {
+          'accountingInfo' => {
+                                #mandatory
+                                'localCurrency' => 'USD',
+                                'currencyConversionInfo' => [
+                                                              {
+                                                                'numberOfDecimalPlaces' => 5,
+                                                                'exchangeRate' => 152549, #???
+                                                                'exchangeRateCode' => 1
+                                                              }
+                                                            ],
+                                'tapDecimalPlaces' => 5,
+                                #optional: may conditionally include taxation and discounting tables, and, optionally, TAP currency
+                              },
+          'batchControlInfo' => {
+                                  #mandatory
+                                  'specificationVersionNumber' => 3,
+                                  'releaseVersionNumber' => 12, #11?
+
+                                  #'sender' => 'MDGTM',
+                                  #'recipient' => 'GNQHT',
+                                  #'fileSequenceNumber' => '00178',
+
+                                  #'transferCutOffTimeStamp' => {
+                                  #                               'localTimeStamp' => '20121230050222',
+                                  #                               'utcTimeOffset' => '+0300'
+                                  #                             },
+                                  #'fileAvailableTimeStamp' => {
+                                  #                              'localTimeStamp' => '20121230035052',
+                                  #                              'utcTimeOffset' => '+0100'
+                                  #                            }
+
+                                  #optional
+                                  #'fileCreationTimeStamp' => {
+                                  #                             'localTimeStamp' => '20121230050222',
+                                  #                             'utcTimeOffset' => '+0300'
+                                  #                           },
+
+                                  #optional: file type indicator which will only be present where the file represents test data
+                                  #optional: RAP File Sequence Number (used where the batch has previously been returned with a fatal error and is now being resubmitted) (not fileSequenceNumber?)
+
+                                  #optional: beyond the scope of TAP and has been bilaterally agreed
+                                  'operatorSpecInformation' => [
+                                                                 '', # XXX '|File proc MTH LUXMA: 1285348027|' Operator Specific Information
+                                                               ],
+             
+
+                                },
+
+          #Network Information is a group of related information which pertains to the Sender PMN
+          'networkInfo' => {
+                             #must be present where Recording Entity Codes are present within the TAP file
+                             'recEntityInfo' => [
+                                                  {
+                                                    'recEntityType' => 1, #MSC
+                                                    #'recEntityId' => '340010100',
+                                                    'recEntityCode' => 1
+                                                  },
+                                                  {
+                                                    'recEntityType' => 2, #SMSC
+                                                    #'recEntityId' => '240556000000',
+                                                    'recEntityCode' => 2
+                                                  },
+                                                ],
+                             #mandatory
+                             'utcTimeOffsetInfo' => [
+                                                      {
+                                                        'utcTimeOffset' => '+0300',
+                                                        'utcTimeOffsetCode' => 1
+                                                      }
+                                                    ]
+                           },
+          'auditControlInfo' => {
+                                  #'callEventDetailsCount' => 4, #mandatory
+                                  'totalTaxValue'         => 0, #mandatory
+                                  'totalDiscountValue'    => 0, #mandatory
+                                  #'totalCharge'           => 50474, #mandatory
+
+                                  #these two are optional
+                                  #'earliestCallTimeStamp' => {
+                                  #                             'localTimeStamp' => '20121229102501',
+                                  #                             'utcTimeOffset' => '+0300'
+                                  #                           },
+                                  #'latestCallTimeStamp'   => {
+                                  #                             'localTimeStamp' => '20121229102807',
+                                  #                             'utcTimeOffset' => '+0300'
+                                  #                           }
+                                },
+}
+
 sub _asn_spec {
   <<'END';
 --
index 1296c1e..bf610c6 100644 (file)
@@ -23,10 +23,11 @@ $DEBUG = 0;
 @pw_set = ( 'a'..'k', 'm','n', 'p-z', 'A'..'N', 'P'..'Z' , '2'..'9' );
 
 #ask FS::UID to run this stuff for us later
-$FS::UID::callback{'FS::svc_acct'} = sub { 
+FS::UID->install_callback( sub { 
   $conf = new FS::Conf;
   $phone_name_max = $conf->config('svc_phone-phone_name-max_length');
-};
+}
+);
 
 =head1 NAME
 
@@ -68,6 +69,10 @@ primary key
 
 =item phonenum
 
+=item sim_imsi
+
+SIM IMSI (http://en.wikipedia.org/wiki/International_mobile_subscriber_identity)
+
 =item sip_password
 
 =item pin
@@ -147,6 +152,7 @@ sub table_info {
                             disable_select => 1,
                           },
         'phonenum'     => 'Phone number',
+        'sim_imsi'     => 'IMSI', #http://en.wikipedia.org/wiki/International_mobile_subscriber_identity
         'pin'          => { label => 'Voicemail PIN', #'Personal Identification Number',
                             type  => 'text',
                             disable_inventory => 1,
@@ -466,6 +472,7 @@ sub check {
     $self->ut_numbern('svcnum')
     || $self->ut_numbern('countrycode')
     || $self->$phonenum_check_method('phonenum')
+    || $self->ut_numbern('sim_imsi')
     || $self->ut_anything('sip_password')
     || $self->ut_numbern('pin')
     || $self->ut_textn('phone_name')
@@ -486,6 +493,10 @@ sub check {
   ;
   return $error if $error;
 
+  return 'Illegal IMSI (not 14-15 digits)' #shorter?
+    if length($self->sim_imsi)
+    && ( length($self->sim_imsi) < 14 || length($self->sim_imsi) > 15 );
+
     # LNP data validation
     return 'Cannot set LNP fields: no LNP in progress'
        if ( ($self->lnp_desired_due_date || $self->lnp_due_date 
index 9647b68..8ee71b8 100644 (file)
@@ -28,6 +28,11 @@ my $begin_callback = sub {
                 type     => 'select-did',
                 label    => 'Phone number',
                 multiple => $bulk,
+              },
+              { field     => 'sim_imsi',
+                type      => 'text',
+                size      => 15,
+                maxlength => 15,
               };
 
   push @$fields, { field => 'domsvc',
index 323be63..e956e7d 100644 (file)
@@ -16,7 +16,7 @@ my %labels = map { $_ =>  ( ref($fields->{$_})
                          );
                  } keys %$fields;
 
-my @fields = qw( countrycode phonenum );
+my @fields = qw( countrycode phonenum sim_imsi );
 push @fields, 'domain' if $conf->exists('svc_phone-domain');
 push @fields, qw( pbx_title sip_password pin phone_name forwarddst email );