L<FS::part_pkg_msgcat> - Package definition localization class
+L<FS::part_pkg_usageprice> - Package definition usage pricing add-on class
+
L<FS::part_pkg_currency> - Package definition local currency prices
L<FS::currency_exchange> - Currency exchange rates
],
},
- 'part_pkg' => {
+ 'part_pkg' => {
'columns' => [
'pkgpart', 'serial', '', '', '', '',
'pkg', 'varchar', '', $char_d, '', '',
'index' => [],
},
+ 'part_pkg_usageprice' => {
+ 'columns' => [
+ 'usagepricepart', 'serial', '', '', '', '',
+ 'pkgpart', 'int', '', '', '', '',
+ 'price', @money_type, '', '',
+ 'currency', 'char', 'NULL', 3, '', '',
+ 'action', 'varchar', '', $char_d, '', '',
+ 'target', 'varchar', '', $char_d, '', '',
+ 'amount', 'varchar', '', $char_d, '', '',
+ ],
+ 'primary_key' => 'usagepricepart',
+ 'unique' => [ [ 'pkgpart', 'currency', 'target' ] ],
+ 'index' => [ [ 'pkgpart' ] ],
+ 'foreign_keys' => [
+ { columns => [ 'pkgpart' ],
+ table => 'part_pkg',
+ },
+ ],
+ },
+
'part_pkg_link' => {
'columns' => [
'pkglinknum', 'serial', '', '', '', '',
--- /dev/null
+package FS::part_pkg_usageprice;
+use base qw( FS::Record );
+
+use strict;
+#use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::part_pkg_usageprice - Object methods for part_pkg_usageprice records
+
+=head1 SYNOPSIS
+
+ use FS::part_pkg_usageprice;
+
+ $record = new FS::part_pkg_usageprice \%hash;
+ $record = new FS::part_pkg_usageprice { 'column' => 'value' };
+
+ $error = $record->insert;
+
+ $error = $new_record->replace($old_record);
+
+ $error = $record->delete;
+
+ $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::part_pkg_usageprice object represents a usage pricing add-on.
+FS::part_pkg_usageprice inherits from FS::Record. The following fields are
+currently supported:
+
+=over 4
+
+=item usagepricepart
+
+primary key
+
+=item pkgpart
+
+pkgpart
+
+=item price
+
+price
+
+=item currency
+
+currency
+
+=item action
+
+action
+
+=item target
+
+target
+
+=item amount
+
+amount
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new record. To add the record to the database, see L<"insert">.
+
+Note that this stores the hash reference, not a distinct copy of the hash it
+points to. You can ask the object for a copy with the I<hash> method.
+
+=cut
+
+sub table { 'part_pkg_usageprice'; }
+
+=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 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('usagepricepart')
+ || $self->ut_number('pkgpart')
+ || $self->ut_money('price')
+ || $self->ut_currencyn('currency')
+ || $self->ut_enum('action', [ 'increment', 'set' ])
+ || $self->ut_enum('target', [ 'svc_acct.totalbytes', 'svc_acct.seconds',
+ 'svc_conferencing.participants',
+ 'svc_conferencing.confqualitynum'
+ ]
+ )
+ || $self->ut_text('amount')
+ ;
+ return $error if $error;
+
+ $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::part_pkg>, L<FS::Record>
+
+=cut
+
+1;
+
}
$conf->config('currencies')
),
+ 'usagepricepart' => ' ',
'discountnum' => 'Offer discounts for longer terms',
'bill_dst_pkgpart' => 'Include line item(s) from package',
'svc_dst_pkgpart' => 'Include services of package',
{ type => 'columnend' },
+ { type => 'tablebreak-tr-title',
+ value => 'Usage pricing add-ons', #better name? just 'Usage pricing' ? there's also CDR usage pricing, RADIUS usage pricing, etc :/
+ },
+ { 'field' => 'usagepricepart',
+ 'type' => 'part_pkg_usageprice',
+ 'o2m_table' => 'part_pkg_usageprice',
+ 'm2_label' => ' ',
+ 'm2_error_callback' => $usageprice_error_callback,
+ },
+
{ 'type' => $report_option ? 'tablebreak-tr-title'
: 'hidden',
'value' => 'Optional report classes',
$cgi->param;
};
+my $usageprice_error_callback = sub {
+ my( $cgi, $object ) = @_;
+ map {
+ if ( /^usagepricepart(\d+)_price$/
+ && $cgi->param("usagepricepart$1_price") )
+ {
+ new FS::part_pkg_usageprice {
+ 'usagepricepart' => $cgi->param("usagepricepart$1"),
+ 'pkgpart' => $object->pkgpart,
+ 'price' => scalar($cgi->param("usagepricepart$1_price")),
+ #'currency
+ 'action' => scalar($cgi->param("usagepricepart$1_action")),
+ 'target' => scalar($cgi->param("usagepricepart$1_target")),
+ 'amount' => scalar($cgi->param("usagepricepart$1_amount")),
+ };
+ } else {
+ ();
+ }
+ }
+ $cgi->param;
+};
+
my $m2_error_callback_maker = sub {
my $link_type = shift; #yay closures
return sub {
'table' => 'part_pkg_msgcat',
'fields' => [qw( locale pkg )],
},
+ {
+ 'table' => 'part_pkg_usageprice',
+ 'fields' => [qw( price currency action target amount )],
+ }
);
</%init>
--- /dev/null
+% unless ( $opt{'js_only'} ) {
+
+ <INPUT TYPE="hidden" NAME="<%$name%>" ID="<%$id%>" VALUE="<% $curr_value %>">
+
+ <TABLE STYLE="display:inline">
+ <TR>
+
+% ###
+% # price
+% ###
+ <TD>
+ <TABLE STYLE="display:inline">
+ <TR>
+ <TD>Price</TD>
+ <TD><% $money_char %><INPUT
+ TYPE = "text"
+ NAME = "<%$name%>_price"
+ ID = "<%$id%>_price"
+ VALUE = "<% scalar($cgi->param($name.'_price'))
+ || $part_pkg_usageprice->price
+ %>"
+ <% $onchange %>
+ ></TD>
+ </TR>
+
+% #XXX lots more work for multi-currency... maybe larger UI changes :/
+% foreach my $currency ( () ) {
+% #foreach my $currency ( sort $conf->config('currencies') ) {
+ <TR>
+ <TD><% $currency %></TD>
+ <TD><% currency_symbol($currency, SYM_HTML) %><INPUT
+ TYPE = "text"
+ NAME = "<%$name%>_price_<%$currency%>"
+ ID = "<%$id%>_price_<%$currency%>"
+ VALUE = "<% scalar($cgi->param($name.'_price_'.$currency))
+ || $part_pkg_usageprice->price #XXX
+ %>"
+ <% $onchange %>
+ ></TD>
+ </TR>
+% }
+
+ </TABLE>
+ </TD>
+
+% ###
+% # action
+% ###
+ <TD>
+ <SELECT NAME = "<%$name%>_action"
+ ID = "<%$id%>_action"
+ <% $onchange %>
+ >
+ <OPTION VALUE="increment">Increment
+%#no set yet <OPTION VALUE="set" <% ($cgi->param($name.'_action') || $part_pkg_usageprice->action) eq 'set' ? 'SELECTED' : '' %>>Set
+ </SELECT>
+ </TD>
+
+% ###
+% # target
+% ###
+ <TD>
+ <SELECT NAME = "<%$name%>_target"
+ ID = "<%$id%>_target"
+ <% $onchange %>
+ >
+% foreach my $target (keys %targets) {
+ <OPTION VALUE="<% $target %>"
+ <% ($cgi->param($name.'_target') || $part_pkg_usageprice->target) eq $target ? 'SELECTED' : '' %>
+ ><% $targets{$target}->{label} %>
+% }
+ </TD>
+
+% ###
+% # amount
+% ###
+ <TD>
+ <INPUT TYPE = "text"
+ NAME = "<%$name%>_amount"
+ ID = "<%$id%>_amount"
+ SIZE = 5
+ VALUE = "<% scalar($cgi->param($name.'_amount'))
+ || $part_pkg_usageprice->amount
+ %>"
+ <% $onchange %>
+ >
+ </TD>
+
+ </TR>
+ </TABLE>
+
+% }
+<%init>
+
+tie my %targets, 'Tie::IxHash', # once?
+ #'svc_acct.totalbytes' => { label => 'Megabytes',
+ # mult => 1048576,
+ # },
+ 'svc_acct.totalbytes' => { label => 'Gigabytes',
+ mult => 1073741824,
+ },
+ 'svc_acct.seconds' => { label => 'Hours',
+ mult => 3600,
+ },
+ 'svc_conferencing.participants' => { label => 'Conference Participants',
+ mult => 1,
+ },
+#this will take more work: set action, not increment..
+# and then value comes from a select, not a text field
+# 'svc_conferencing.confqualitynum' => { label => 'Conference Quality',
+# },
+;
+
+my( %opt ) = @_;
+
+my $conf = new FS::Conf;
+my $money_char = $conf->config('money_char') || '$';
+
+my $name = $opt{'element_name'} || $opt{'field'} || 'usagepricepart';
+my $id = $opt{'id'} || 'usagepricepart';
+
+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 $part_pkg_usageprice = $curr_value
+ ? qsearchs('part_pkg_usageprice', { 'usagepricepart' => $curr_value } )
+ : new FS::part_pkg_usageprice {};
+
+</%init>
--- /dev/null
+% unless ( $opt{'js_only'} ) {
+
+ <% include('tr-td-label.html', %opt) %>
+ <TD <% $cell_style %>>
+
+% }
+%
+ <% include( '/elements/part_pkg_usageprice.html', %opt ) %>
+%
+% unless ( $opt{'js_only'} ) {
+
+ </TD>
+ </TR>
+
+% }
+<%init>
+
+my( %opt ) = @_;
+
+my $cell_style = $opt{'cell_style'} ? 'STYLE="'. $opt{'cell_style'}. '"' : '';
+
+#$opt{'label'} ||= 'XXX Something';
+
+</%init>