[freeside-commits] branch master updated. 34c878349988d97957f1d53427896a4d70afb392

Mark Wells mark at 420.am
Tue Aug 2 13:17:16 PDT 2016


The branch, master has been updated
       via  34c878349988d97957f1d53427896a4d70afb392 (commit)
      from  4f3d9e2ef5ce5305363ae426b87ed2b873b355d8 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 34c878349988d97957f1d53427896a4d70afb392
Author: Mark Wells <mark at freeside.biz>
Date:   Tue Aug 2 11:41:51 2016 -0700

    agent commission schedules for consecutive invoices, #71217

diff --git a/FS/FS/Mason.pm b/FS/FS/Mason.pm
index 1008fd5..245bdea 100644
--- a/FS/FS/Mason.pm
+++ b/FS/FS/Mason.pm
@@ -413,6 +413,8 @@ if ( -e $addl_handler_use_file ) {
   use FS::olt_site;
   use FS::access_user_page_pref;
   use FS::part_svc_msgcat;
+  use FS::commission_schedule;
+  use FS::commission_rate;
   # Sammath Naur
 
   if ( $FS::Mason::addl_handler_use ) {
diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index ac58510..8661c4b 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1361,6 +1361,7 @@ sub tables_hashref {
         'commission_agentnum', 'int', 'NULL', '', '', '', #
         'commission_salesnum', 'int', 'NULL', '', '', '', #
         'commission_pkgnum',   'int', 'NULL', '', '', '', #
+        'commission_invnum',   'int', 'NULL', '', '', '',
         'credbatch',    'varchar', 'NULL', $char_d, '', '',
       ],
       'primary_key'  => 'crednum',
@@ -1396,6 +1397,10 @@ sub tables_hashref {
                             table      => 'cust_pkg',
                             references => [ 'pkgnum' ],
                           },
+                          { columns    => [ 'commission_invnum' ],
+                            table      => 'cust_bill',
+                            references => [ 'invnum' ],
+                          },
                         ],
     },
 
@@ -1417,6 +1422,7 @@ sub tables_hashref {
         'commission_agentnum', 'int', 'NULL', '', '', '',
         'commission_salesnum', 'int', 'NULL', '', '', '',
         'commission_pkgnum',   'int', 'NULL', '', '', '',
+        'commission_invnum',   'int', 'NULL', '', '', '',
         #void fields
         'void_date',  @date_type,                  '', '', 
         'void_reason', 'varchar', 'NULL', $char_d, '', '', 
@@ -1456,6 +1462,10 @@ sub tables_hashref {
                             table      => 'cust_pkg',
                             references => [ 'pkgnum' ],
                           },
+                          { columns    => [ 'commission_invnum' ],
+                            table      => 'cust_bill',
+                            references => [ 'invnum' ],
+                          },
                           { columns    => [ 'void_reasonnum' ],
                             table      => 'reason',
                             references => [ 'reasonnum' ],
@@ -7438,6 +7448,36 @@ sub tables_hashref {
                         ],
     },
 
+    'commission_schedule' => {
+      'columns' => [
+        'schedulenum',    'serial',     '',      '', '', '',
+        'schedulename',  'varchar',     '', $char_d, '', '',
+        'reasonnum',         'int', 'NULL',      '', '', '',
+        'basis',         'varchar', 'NULL',      32, '', '',
+      ],
+      'primary_key'  => 'schedulenum',
+      'unique'       => [],
+      'index'        => [],
+    },
+
+    'commission_rate' => {
+      'columns' => [
+        'commissionratenum', 'serial',     '',      '', '', '',
+        'schedulenum',       'int',     '',      '', '', '',
+        'cycle',             'int',     '',      '', '', '',
+        'amount',            @money_type,          '', '', 
+        'percent',           'decimal','',   '7,4', '', '',
+      ],
+      'primary_key'  => 'commissionratenum',
+      'unique'       => [ [ 'schedulenum', 'cycle', ] ],
+      'index'        => [],
+      'foreign_keys' => [
+                          { columns => [ 'schedulenum' ],
+                            table   => 'commission_schedule',
+                          },
+                        ],
+    },
+ 
     # name type nullability length default local
 
     #'new_table' => {
diff --git a/FS/FS/commission_rate.pm b/FS/FS/commission_rate.pm
new file mode 100644
index 0000000..dcb596d
--- /dev/null
+++ b/FS/FS/commission_rate.pm
@@ -0,0 +1,116 @@
+package FS::commission_rate;
+use base qw( FS::Record );
+
+use strict;
+use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::commission_rate - Object methods for commission_rate records
+
+=head1 SYNOPSIS
+
+  use FS::commission_rate;
+
+  $record = new FS::commission_rate \%hash;
+  $record = new FS::commission_rate { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::commission_rate object represents a commission rate (a percentage or a
+flat amount) that will be paid on a customer's N-th invoice. The sequence of
+commissions that will be paid on consecutive invoices is the parent object,
+L<FS::commission_schedule>.
+
+FS::commission_rate inherits from FS::Record.  The following fields are
+currently supported:
+
+=over 4
+
+=item commissionratenum - primary key
+
+=item schedulenum - L<FS::commission_schedule> foreign key
+
+=item cycle - the ordinal of the billing cycle this commission will apply
+to. cycle = 1 applies to the customer's first invoice, cycle = 2 to the
+second, etc.
+
+=item amount - the flat amount to pay per invoice in commission
+
+=item percent - the percentage of the invoice amount to pay in 
+commission
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new commission rate.  To add it to the database, see L<"insert">.
+
+=cut
+
+sub table { 'commission_rate'; }
+
+=item insert
+
+Adds this record to the database.  If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database.  If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid commission rate.  If there is
+an error, returns the error, otherwise returns false.  Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+  my $self = shift;
+
+  $self->set('amount', '0.00')
+    if $self->get('amount') eq '';
+  $self->set('percent', '0')
+    if $self->get('percent') eq '';
+
+  my $error = 
+    $self->ut_numbern('commissionratenum')
+    || $self->ut_number('schedulenum')
+    || $self->ut_number('cycle')
+    || $self->ut_money('amount')
+    || $self->ut_decimal('percent')
+  ;
+  return $error if $error;
+
+  $self->SUPER::check;
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/FS/FS/commission_schedule.pm b/FS/FS/commission_schedule.pm
new file mode 100644
index 0000000..375386c
--- /dev/null
+++ b/FS/FS/commission_schedule.pm
@@ -0,0 +1,235 @@
+package FS::commission_schedule;
+use base qw( FS::o2m_Common FS::Record );
+
+use strict;
+use FS::Record qw( qsearch qsearchs );
+use FS::commission_rate;
+use Tie::IxHash;
+
+tie our %basis_options, 'Tie::IxHash', (
+  setuprecur    => 'Total sales',
+  setup         => 'One-time and setup charges',
+  recur         => 'Recurring charges',
+  setup_cost    => 'Setup costs',
+  recur_cost    => 'Recurring costs',
+  setup_margin  => 'Setup charges minus costs',
+  recur_margin_permonth => 'Monthly recurring charges minus costs',
+);
+
+=head1 NAME
+
+FS::commission_schedule - Object methods for commission_schedule records
+
+=head1 SYNOPSIS
+
+  use FS::commission_schedule;
+
+  $record = new FS::commission_schedule \%hash;
+  $record = new FS::commission_schedule { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::commission_schedule object represents a bundle of one or more
+commission rates for invoices. FS::commission_schedule inherits from
+FS::Record.  The following fields are currently supported:
+
+=over 4
+
+=item schedulenum - primary key
+
+=item schedulename - descriptive name
+
+=item reasonnum - the credit reason (L<FS::reason>) that will be assigned
+to these commission credits
+
+=item basis - for percentage credits, which component of the invoice charges
+the percentage will be calculated on:
+- setuprecur (total charges)
+- setup
+- recur
+- setup_cost
+- recur_cost
+- setup_margin (setup - setup_cost)
+- recur_margin_permonth ((recur - recur_cost) / freq)
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new commission schedule.  To add the object to the database, see
+L<"insert">.
+
+=cut
+
+sub table { 'commission_schedule'; }
+
+=item insert
+
+Adds this record to the database.  If there is an error, returns the error,
+otherwise returns false.
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+sub delete {
+  my $self = shift;
+  # don't allow the schedule to be removed if it's still linked to events
+  if ($self->part_event) {
+    return 'This schedule is still in use.'; # UI should be smarter
+  }
+  $self->process_o2m(
+    'table'   => 'commission_rate',
+    'params'  => [],
+  ) || $self->delete;
+}
+
+=item replace OLD_RECORD
+
+Replaces the OLD_RECORD with this one in the database.  If there is an error,
+returns the error, otherwise returns false.
+
+=item check
+
+Checks all fields to make sure this is a valid record.  If there is
+an error, returns the error, otherwise returns false.  Called by the insert
+and replace methods.
+
+=cut
+
+sub check {
+  my $self = shift;
+
+  my $error = 
+    $self->ut_numbern('schedulenum')
+    || $self->ut_text('schedulename')
+    || $self->ut_number('reasonnum')
+    || $self->ut_enum('basis', [ keys %basis_options ])
+  ;
+  return $error if $error;
+
+  $self->SUPER::check;
+}
+
+=item part_event
+
+Returns a list of billing events (L<FS::part_event> objects) that pay
+commission on this schedule.
+
+=cut
+
+sub part_event {
+  my $self = shift;
+  map { $_->part_event }
+    qsearch('part_event_option', {
+      optionname  => 'schedulenum',
+      optionvalue => $self->schedulenum,
+    }
+  );
+}
+
+=item calc_credit INVOICE
+
+Takes an L<FS::cust_bill> object and calculates credit on this schedule.
+Returns the amount to credit. If there's no rate defined for this invoice,
+returns nothing.
+
+=cut
+
+# Some false laziness w/ FS::part_event::Action::Mixin::credit_bill.
+# this is a little different in that we calculate the credit on the whole
+# invoice.
+
+sub calc_credit {
+  my $self = shift;
+  my $cust_bill = shift;
+  die "cust_bill record required" if !$cust_bill or !$cust_bill->custnum;
+  # count invoices before or including this one
+  my $cycle = FS::cust_bill->count('custnum = ? AND _date <= ?',
+    $cust_bill->custnum,
+    $cust_bill->_date
+  );
+  my $rate = qsearchs('commission_rate', {
+    schedulenum => $self->schedulenum,
+    cycle       => $cycle,
+  });
+  # we might do something with a rate that applies "after the end of the
+  # schedule" (cycle = 0 or something) so that this can do commissions with
+  # no end date. add that here if there's a need.
+  return unless $rate;
+
+  my $amount;
+  if ( $rate->percent ) {
+    my $what = $self->basis;
+    my $cost = ($what =~ /_cost/ ? 1 : 0);
+    my $margin = ($what =~ /_margin/ ? 1 : 0);
+    my %part_pkg_cache;
+    foreach my $cust_bill_pkg ( $cust_bill->cust_bill_pkg ) {
+
+      my $charge = 0;
+      next if !$cust_bill_pkg->pkgnum; # exclude taxes and fees
+
+      my $cust_pkg = $cust_bill_pkg->cust_pkg;
+      if ( $margin or $cost ) {
+        # look up package costs only if we need them
+        my $pkgpart = $cust_bill_pkg->pkgpart_override || $cust_pkg->pkgpart;
+        my $part_pkg   = $part_pkg_cache{$pkgpart}
+                     ||= FS::part_pkg->by_key($pkgpart);
+
+        if ( $cost ) {
+          $charge = $part_pkg->get($what);
+        } else { # $margin
+          $charge = $part_pkg->$what($cust_pkg);
+        }
+
+        $charge = ($charge || 0) * ($cust_pkg->quantity || 1);
+
+      } else {
+
+        if ( $what eq 'setup' ) {
+          $charge = $cust_bill_pkg->get('setup');
+        } elsif ( $what eq 'recur' ) {
+          $charge = $cust_bill_pkg->get('recur');
+        } elsif ( $what eq 'setuprecur' ) {
+          $charge = $cust_bill_pkg->get('setup') +
+                    $cust_bill_pkg->get('recur');
+        }
+      }
+
+      $amount += ($charge * $rate->percent / 100);
+
+    }
+  } # if $rate->percent
+
+  if ( $rate->amount ) {
+    $amount += $rate->amount;
+  }
+
+  $amount = sprintf('%.2f', $amount + 0.005);
+  return $amount;
+}
+
+=back
+
+=head1 SEE ALSO
+
+L<FS::Record>, L<FS::part_event>, L<FS::commission_rate>
+
+=cut
+
+1;
+
diff --git a/FS/FS/cust_credit.pm b/FS/FS/cust_credit.pm
index 8546372..e4b1fc0 100644
--- a/FS/FS/cust_credit.pm
+++ b/FS/FS/cust_credit.pm
@@ -315,6 +315,7 @@ sub check {
     || $self->ut_foreign_keyn('commission_agentnum',  'agent', 'agentnum')
     || $self->ut_foreign_keyn('commission_salesnum',  'sales', 'salesnum')
     || $self->ut_foreign_keyn('commission_pkgnum', 'cust_pkg', 'pkgnum')
+    || $self->ut_foreign_keyn('commission_invnum', 'cust_bill', 'invnum')
   ;
   return $error if $error;
 
diff --git a/FS/FS/part_event/Action/bill_agent_credit_schedule.pm b/FS/FS/part_event/Action/bill_agent_credit_schedule.pm
new file mode 100644
index 0000000..31189a2
--- /dev/null
+++ b/FS/FS/part_event/Action/bill_agent_credit_schedule.pm
@@ -0,0 +1,76 @@
+package FS::part_event::Action::bill_agent_credit_schedule;
+
+use base qw( FS::part_event::Action );
+use FS::Conf;
+use FS::cust_credit;
+use FS::commission_schedule;
+use Date::Format qw(time2str);
+
+use strict;
+
+sub description { 'Credit the agent based on a commission schedule' }
+
+sub option_fields {
+  'schedulenum' => { 'label'        => 'Schedule',
+                     'type'         => 'select-table',
+                     'table'        => 'commission_schedule',
+                     'name_col'     => 'schedulename',
+                     'disable_empty'=> 1,
+                   },
+}
+
+sub eventtable_hashref {
+  { 'cust_bill' => 1 };
+}
+
+our $date_format;
+
+sub do_action {
+  my( $self, $cust_bill, $cust_event ) = @_;
+
+  $date_format ||= FS::Conf->new->config('date_format') || '%x';
+
+  my $cust_main = $self->cust_main($cust_bill);
+  my $agent = $cust_main->agent;
+  return "No customer record for agent ". $agent->agent
+    unless $agent->agent_custnum;
+
+  my $agent_cust_main = $agent->agent_cust_main;
+
+  my $schedulenum = $self->option('schedulenum')
+    or return "no commission schedule selected";
+  my $schedule = FS::commission_schedule->by_key($schedulenum)
+    or return "commission schedule #$schedulenum not found";
+    # commission_schedule::delete tries to prevent this, but just in case
+
+  my $amount = $schedule->calc_credit($cust_bill)
+    or return;
+
+  my $reasonnum = $schedule->reasonnum;
+
+  #XXX shouldn't do this here, it's a localization problem.
+  # credits with commission_invnum should know how to display it as part
+  # of invoice rendering.
+  my $desc = 'from invoice #'. $cust_bill->display_invnum .
+             ' ('. time2str($date_format, $cust_bill->_date) . ')';
+             # could also show custnum and pkgnums here?
+  my $cust_credit = FS::cust_credit->new({
+    'custnum'             => $agent_cust_main->custnum,
+    'reasonnum'           => $reasonnum,
+    'amount'              => $amount,
+    'eventnum'            => $cust_event->eventnum,
+    'addlinfo'            => $desc,
+    'commission_agentnum' => $cust_main->agentnum,
+    'commission_invnum'   => $cust_bill->invnum,
+  });
+  my $error = $cust_credit->insert;
+  die "Error crediting customer ". $agent_cust_main->custnum.
+      " for agent commission: $error"
+    if $error;
+
+  #return $warning; # currently don't get warnings here
+  return;
+
+}
+
+1;
diff --git a/FS/MANIFEST b/FS/MANIFEST
index 83359f1..4184b9c 100644
--- a/FS/MANIFEST
+++ b/FS/MANIFEST
@@ -870,3 +870,7 @@ FS/webservice_log.pm
 t/webservice_log.t
 FS/access_user_page_pref.pm
 t/access_user_page_pref.t
+FS/commission_schedule.pm
+t/commission_schedule.t
+FS/commission_rate.pm
+t/commission_rate.t
diff --git a/FS/t/commission_rate.t b/FS/t/commission_rate.t
new file mode 100644
index 0000000..fb5f43c
--- /dev/null
+++ b/FS/t/commission_rate.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::commission_rate;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/commission_schedule.t b/FS/t/commission_schedule.t
new file mode 100644
index 0000000..bbe6b42
--- /dev/null
+++ b/FS/t/commission_schedule.t
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::commission_schedule;
+$loaded=1;
+print "ok 1\n";
diff --git a/httemplate/browse/commission_schedule.html b/httemplate/browse/commission_schedule.html
new file mode 100644
index 0000000..5a4f984
--- /dev/null
+++ b/httemplate/browse/commission_schedule.html
@@ -0,0 +1,70 @@
+<& elements/browse.html,
+  'title'       => "Commission schedules",
+  'name'        => "commission schedules",
+  'menubar'     => [ 'Add a new schedule' =>
+                        $p.'edit/commission_schedule.html'
+                   ],
+  'query'       => { 'table'     => 'commission_schedule', },
+  'count_query' => 'SELECT COUNT(*) FROM commission_schedule',
+  'header'      => [ '#',
+                     'Name',
+                     'Rates',
+                   ],
+  'fields'      => [ 'schedulenum',
+                     'schedulename',
+                     $rates_sub,
+                  ],
+  'links'       => [ $link,
+                     $link,
+                     '',
+                   ],
+  'disable_total' => 1,
+&>
+<%init>
+
+my $money_char = FS::Conf->new->config('money_char') || '$';
+
+my $ordinal_sub = sub {
+  # correct from 1 to 12...
+  my $num = shift;
+  $num == 1 ? '1st' :
+  $num == 2 ? '2nd' :
+  $num == 3 ? '3rd' :
+  $num . 'th'
+};
+
+my $rates_sub = sub {
+  my $schedule = shift;
+  my @rates = sort { $a->cycle <=> $b->cycle } $schedule->commission_rate;
+  my @data;
+  my $basis = emt(lc( $FS::commission_schedule::basis_options{$schedule->basis} ));
+  foreach my $rate (@rates) {
+    my $desc = '';
+    if ( $rate->amount > 0 ) {
+      $desc = $money_char . sprintf('%.2f', $rate->amount);
+    }
+    if ( $rate->percent > 0 ) {
+      $desc .= ' + ' if $desc;
+      $desc .= $rate->percent . '% ' . emt('of') . ' ' . $basis;
+    }
+    next if !$desc;
+    $desc = &$ordinal_sub($rate->cycle) . ' ' . emt('invoice') .
+             ': ' . $desc;
+
+    push @data,
+    [
+      {
+        'data'  => $desc,
+        'align' => 'right',
+      }
+    ];
+  }
+  \@data;
+};
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $link = [ $p.'edit/commission_schedule.html?', 'schedulenum' ];
+
+</%init>
diff --git a/httemplate/edit/commission_schedule.html b/httemplate/edit/commission_schedule.html
new file mode 100644
index 0000000..c76a361
--- /dev/null
+++ b/httemplate/edit/commission_schedule.html
@@ -0,0 +1,53 @@
+<& elements/edit.html,
+     name_singular => 'schedule',
+     table         => 'commission_schedule',
+     viewall_dir   => 'browse',
+     fields        => [ 'schedulename',
+                        { field             => 'reasonnum',
+                          type              => 'select-reason',
+                          reason_class      => 'R',
+                        },
+                        { field             => 'basis',
+                          type              => 'select',
+                          options           => [ keys %FS::commission_schedule::basis_options ],
+                          labels            => { %FS::commission_schedule::basis_options },
+                        },
+                        { type => 'tablebreak-tr-title', value => 'Billing cycles' },
+                        { field             => 'commissionratenum',
+                          type              => 'commission_rate',
+                          o2m_table         => 'commission_rate',
+                          m2_label          => ' ',
+                          m2_error_callback => $m2_error_callback,
+                          colspan => 2,
+                        },
+                      ],
+     labels        => { 'schedulenum'       => '',
+                        'schedulename'      => 'Name',
+                        'basis'             => 'Based on',
+                        'commissionratenum' => '',
+                      },
+&>
+<%init>
+
+my $m2_error_callback = sub {
+  my ($cgi, $object) = @_;
+
+  my @rates;
+  foreach my $k ( grep /^commissionratenum\d+/, $cgi->param ) {
+    my $num = $cgi->param($k);
+    my $cycle = $cgi->param($k.'_cycle');
+    my $amount = $cgi->param($k.'_amount');
+    my $percent = $cgi->param($k.'_percent');
+    if ($cycle > 0) {
+      push @rates, FS::commission_rate->new({
+        'commissionratenum' => $num,
+        'cycle'             => $cycle,
+        'amount'            => $amount,
+        'percent'           => $percent,
+      });
+    }
+  }
+  @rates;
+};
+
+</%init>
diff --git a/httemplate/edit/process/commission_schedule.html b/httemplate/edit/process/commission_schedule.html
new file mode 100644
index 0000000..50e0371
--- /dev/null
+++ b/httemplate/edit/process/commission_schedule.html
@@ -0,0 +1,36 @@
+<& elements/process.html,
+  'table'       => 'commission_schedule',
+  'viewall_dir' => 'browse',
+  'process_o2m' => {
+   'table'  => 'commission_rate',
+   'fields' => [qw( cycle amount percent )],
+  },
+  'precheck_callback' => $precheck,
+  'debug' => 1,
+&>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Configuration');
+
+my $precheck = sub {
+  my $cgi = shift;
+  $cgi->param('reasonnum') =~ /^(-?\d+)$/ or die "Illegal reasonnum";
+
+  my ($reasonnum, $error) = $m->comp('/misc/process/elements/reason');
+  if (!$reasonnum) {
+    $error ||= 'Reason required'
+  }
+  $cgi->param('reasonnum', $reasonnum) unless $error;
+
+  # remove rate entries with no cycle selected
+  foreach my $k (grep /^commissionratenum\d+$/, $cgi->param) {
+    if (! $cgi->param($k.'_cycle') ) {
+      $cgi->delete($k);
+    }
+  }
+
+  $error;
+};
+
+</%init>
diff --git a/httemplate/elements/commission_rate.html b/httemplate/elements/commission_rate.html
new file mode 100644
index 0000000..071ebb1
--- /dev/null
+++ b/httemplate/elements/commission_rate.html
@@ -0,0 +1,68 @@
+% unless ( $opt{'js_only'} ) {
+
+  <INPUT TYPE="hidden" NAME="<%$name%>" ID="<%$id%>" VALUE="<% $curr_value %>">
+
+      <& select.html,
+        field         => "${name}_cycle",
+        options       => [ '', 1 .. 12 ],
+        option_labels => {
+          ''  => '',
+          1   => '1st',
+          2   => '2nd',
+          3   => '3rd',
+          map { $_ => $_.'th' } 4 .. 12
+        },
+        onchange      => $onchange,
+        curr_value    => $commission_rate->get("cycle"),
+      &>
+      <B><% $money_char %></B>
+      <& input-text.html,
+        field         => "${name}_amount",
+        size          => 8,
+        curr_value    => $commission_rate->get("amount")
+                         || '0.00',
+        'text-align'  => 'right'
+      &>
+      <B> + </B>
+      <& input-text.html,
+        field         => "${name}_percent",
+        size          => 8,
+        curr_value    => $commission_rate->get("percent")
+                         || '0',
+        'text-align'  => 'right'
+      &><B>%</B>
+% }
+<%init>
+
+my( %opt ) = @_;
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $name = $opt{'field'} || 'commissionratenum';
+my $id = $opt{'id'} || 'commissionratenum';
+
+my $curr_value = $opt{'curr_value'} || $opt{'value'};
+
+my $onchange = '';
+if ( $opt{'onchange'} ) {
+  $onchange = $opt{'onchange'};
+  $onchange .= '(this)' unless $onchange =~ /\(\w*\);?$/;
+  $onchange =~ s/\(what\);/\(this\);/g; #ugh, terrible hack.  all onchange
+                                        #callbacks should act the same
+  $onchange = 'onChange="'. $onchange. '"';
+}
+
+my $commission_rate;
+if ( $curr_value ) {
+  $commission_rate = qsearchs('commission_rate', { 'commissionratenum' => $curr_value } );
+} else {
+  $commission_rate = new FS::commission_rate {};
+}
+
+foreach my $field (qw( amount percent cycle)) {
+  my $value = $cgi->param("${name}_${field}");
+  $commission_rate->set($field, $value) if $value;
+}
+
+</%init>
diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index 0f98bc9..88c1df3 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -672,7 +672,10 @@ $config_cust{'Note classes'} = [ $fsurl.'browse/cust_note_class.html', 'Note cla
 tie my %config_agent, 'Tie::IxHash',
   'Agent types' => [ $fsurl.'browse/agent_type.cgi', 'Agent types define groups of package definitions that you can then assign to particular agents' ],
   'Agents'      => [ $fsurl.'browse/agent.cgi', 'Agents are resellers of your service. Agents may be limited to a subset of your full offerings (via their type)' ],
-  'Agent payment gateways'         => [ $fsurl.'browse/payment_gateway.html', 'Credit card and electronic check processors for agent overrides' ];
+  'Agent payment gateways'         => [ $fsurl.'browse/payment_gateway.html', 'Credit card and electronic check processors for agent overrides' ],
+  'separator' => '',
+  'Commission schedules' => [ $fsurl.'browse/commission_schedule.html',
+    'Commission schedules for consecutive billing periods' ],
 ;
 
 tie my %config_sales, 'Tie::IxHash',
diff --git a/httemplate/elements/tr-select-reason.html b/httemplate/elements/tr-select-reason.html
index 97466f1..9a43022 100755
--- a/httemplate/elements/tr-select-reason.html
+++ b/httemplate/elements/tr-select-reason.html
@@ -188,9 +188,8 @@ my $class = $opt{'reason_class'};
 my $init_reason;
 if ( $opt{'cgi'} ) {
   $init_reason = $opt{'cgi'}->param($name);
-} else {
-  $init_reason = $opt{'curr_value'};
 }
+$init_reason ||= $opt{'curr_value'};
 
 my $id = $opt{'id'} || $name;
 $id =~ s/\./_/g; # for edit/part_event

-----------------------------------------------------------------------

Summary of changes:
 FS/FS/Mason.pm                                     |    2 +
 FS/FS/Schema.pm                                    |   40 ++++
 FS/FS/commission_rate.pm                           |  116 ++++++++++
 FS/FS/commission_schedule.pm                       |  235 ++++++++++++++++++++
 FS/FS/cust_credit.pm                               |    1 +
 .../Action/bill_agent_credit_schedule.pm           |   76 +++++++
 FS/MANIFEST                                        |    4 +
 FS/t/{AccessRight.t => commission_rate.t}          |    2 +-
 FS/t/{AccessRight.t => commission_schedule.t}      |    2 +-
 httemplate/browse/commission_schedule.html         |   70 ++++++
 httemplate/edit/commission_schedule.html           |   53 +++++
 httemplate/edit/process/commission_schedule.html   |   36 +++
 httemplate/elements/commission_rate.html           |   68 ++++++
 httemplate/elements/menu.html                      |    5 +-
 httemplate/elements/tr-select-reason.html          |    3 +-
 15 files changed, 708 insertions(+), 5 deletions(-)
 create mode 100644 FS/FS/commission_rate.pm
 create mode 100644 FS/FS/commission_schedule.pm
 create mode 100644 FS/FS/part_event/Action/bill_agent_credit_schedule.pm
 copy FS/t/{AccessRight.t => commission_rate.t} (80%)
 copy FS/t/{AccessRight.t => commission_schedule.t} (77%)
 create mode 100644 httemplate/browse/commission_schedule.html
 create mode 100644 httemplate/edit/commission_schedule.html
 create mode 100644 httemplate/edit/process/commission_schedule.html
 create mode 100644 httemplate/elements/commission_rate.html




More information about the freeside-commits mailing list