[freeside-commits] branch master updated. b6f16a22bd93ec66ffbb1da30e63f7e950b3b819

Jonathan Prykop jonathan at 420.am
Thu Oct 15 19:08:46 PDT 2015


The branch, master has been updated
       via  b6f16a22bd93ec66ffbb1da30e63f7e950b3b819 (commit)
      from  2b2dd969f3c18751afc583ad1e836ab8e6f73b5d (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 b6f16a22bd93ec66ffbb1da30e63f7e950b3b819
Author: Jonathan Prykop <jonathan at freeside.biz>
Date:   Thu Oct 15 21:07:34 2015 -0500

    RT#34960: Quotations

diff --git a/FS/FS/Schema.pm b/FS/FS/Schema.pm
index 486860f..479ab10 100644
--- a/FS/FS/Schema.pm
+++ b/FS/FS/Schema.pm
@@ -1962,6 +1962,24 @@ sub tables_hashref {
                         ],
     },
 
+    'quotation_pkg_detail' => {
+      'columns' => [
+        'detailnum', 'serial', '', '', '', '', 
+        'billpkgnum', 'int', '', '', '', '',        # actually links to quotationpkgnum
+        'format',  'char', 'NULL', 1, '', '',       # not used for anything
+        'detail',  'varchar', '', 255, '', '',
+      ],
+      'primary_key'  => 'detailnum',
+      'unique'       => [],
+      'index'        => [ [ 'billpkgnum' ] ],
+      'foreign_keys' => [
+                          { columns    => [ 'billpkgnum' ],
+                            table      => 'quotation_pkg',
+                            references => [ 'quotationpkgnum' ],
+                          },
+                        ],
+    },
+
     'quotation_pkg_discount' => {
       'columns' => [
         'quotationpkgdiscountnum', 'serial', '', '', '', '',
diff --git a/FS/FS/quotation.pm b/FS/FS/quotation.pm
index f820510..d66b1b8 100644
--- a/FS/FS/quotation.pm
+++ b/FS/FS/quotation.pm
@@ -972,11 +972,12 @@ sub _items_pkg {
 
   foreach my $quotation_pkg (@pkgs) {
     my $part_pkg = $quotation_pkg->part_pkg;
+    my @details = $quotation_pkg->details;
     my $setuprecur;
     my $this_item = {
       'pkgnum'          => $quotation_pkg->quotationpkgnum,
       'description'     => $quotation_pkg->desc($locale),
-      'ext_description' => [],
+      'ext_description' => \@details,
       'quantity'        => $quotation_pkg->quantity,
     };
     if ($freq eq '0') {
diff --git a/FS/FS/quotation_pkg.pm b/FS/FS/quotation_pkg.pm
index 4c78be7..10bdc2e 100644
--- a/FS/FS/quotation_pkg.pm
+++ b/FS/FS/quotation_pkg.pm
@@ -5,6 +5,7 @@ use strict;
 use FS::Record qw( qsearchs qsearch dbh );
 use FS::part_pkg;
 use FS::quotation_pkg_discount; #so its loaded when TemplateItem_Mixin needs it
+use FS::quotation_pkg_detail;
 use List::Util qw(sum);
 
 =head1 NAME
@@ -101,6 +102,20 @@ sub display_table         { 'quotation_pkg'; }
 
 sub discount_table        { 'quotation_pkg_discount'; }
 
+# detail table uses non-quotation fieldnames, see billpkgnum below
+sub detail_table          { 'quotation_pkg_detail'; }
+
+=item billpkgnum
+
+Sets/returns quotationpkgnum, for ease of integration with TemplateItem_Mixin::details
+
+=cut
+
+sub billpkgnum {
+  my $self = shift;
+  $self->quotationpkgnum(@_);
+}
+
 =item insert
 
 Adds this record to the database.  If there is an error, returns the error,
@@ -145,15 +160,21 @@ sub delete {
   my $oldAutoCommit = $FS::UID::AutoCommit;
   local $FS::UID::AutoCommit = 0;
 
+  my $error = $self->delete_details;
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
   foreach ($self->quotation_pkg_discount, $self->quotation_pkg_tax) {
-    my $error = $_->delete;
+    $error = $_->delete;
     if ( $error ) {
       $dbh->rollback if $oldAutoCommit;
       return $error . ' (deleting discount)';
     }
   }
 
-  my $error = $self->SUPER::delete;
+  $error = $self->SUPER::delete;
   if ( $error ) {
     $dbh->rollback if $oldAutoCommit;
     return $error;
@@ -329,6 +350,69 @@ sub part_pkg_currency_option {
   }
 }
 
+=item delete_details
+
+Deletes all quotation_pkgs_details associated with this pkg (see L<FS::quotation_pkg_detail>).
+
+=cut
+
+sub delete_details {
+  my $self = shift;
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  foreach my $detail ( qsearch('quotation_pkg_detail',{ 'billpkgnum' => $self->quotationpkgnum }) ) {
+    my $error = $detail->delete;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "error removing old detail: $error";
+    }
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
+
+}
+
+=item set_details [ DETAIL, DETAIL, ... ]
+
+Sets quotation details for this package (see L<FS::quotation_pkg_detail>).
+
+If there is an error, returns the error, otherwise returns false.
+
+=cut
+
+sub set_details {
+  my( $self, @details ) = @_;
+
+  my $oldAutoCommit = $FS::UID::AutoCommit;
+  local $FS::UID::AutoCommit = 0;
+  my $dbh = dbh;
+
+  my $error = $self->delete_details;
+  if ( $error ) {
+    $dbh->rollback if $oldAutoCommit;
+    return $error;
+  }
+
+  foreach my $detail ( @details ) {
+    my $quotation_pkg_detail = new FS::quotation_pkg_detail {
+      'billpkgnum' => $self->quotationpkgnum,
+      'detail'     => $detail,
+    };
+    $error = $quotation_pkg_detail->insert;
+    if ( $error ) {
+      $dbh->rollback if $oldAutoCommit;
+      return "error adding new detail: $error";
+    }
+  }
+
+  $dbh->commit or die $dbh->errstr if $oldAutoCommit;
+  '';
+
+}
 
 =item cust_bill_pkg_display [ type => TYPE ]
 
diff --git a/FS/FS/quotation_pkg_detail.pm b/FS/FS/quotation_pkg_detail.pm
new file mode 100644
index 0000000..be3d815
--- /dev/null
+++ b/FS/FS/quotation_pkg_detail.pm
@@ -0,0 +1,130 @@
+package FS::quotation_pkg_detail;
+use base qw(FS::Record);
+
+use strict;
+
+=head1 NAME
+
+FS::quotation_pkg_detail - Object methods for quotation_pkg_detail records
+
+=head1 SYNOPSIS
+
+  use FS::quotation_pkg_detail;
+
+  $record = new FS::quotation_pkg_detail \%hash;
+  $record = new FS::quotation_pkg_detail { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::quotation_pkg_detail object represents additional customer package details
+for a quotation.  FS::quotation_pkg_detail inherits from FS::Record.  The following fields are
+currently supported:
+
+=over 4
+
+=item detailnum
+
+primary key
+
+=item billpkgnum
+
+named thusly for quick compatability with L<FS::TemplateItem_Mixin>,
+actually the quotationpkgnum for the relevant L<FS::quotation_pkg>
+
+=item detail
+
+detail text
+
+=cut
+
+# 'format' field isn't used, there for TemplateItem_Mixin
+
+=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
+
+# the new method can be inherited from FS::Record, if a table method is defined
+
+sub table { 'quotation_pkg_detail'; }
+
+=item insert
+
+Adds this record to the database.  If there is an error, returns the error,
+otherwise returns false.
+
+=cut
+
+# the insert method can be inherited from FS::Record
+
+=item delete
+
+Delete this record from the database.
+
+=cut
+
+# the delete method can be inherited from FS::Record
+
+=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.
+
+=cut
+
+# the replace method can be inherited from FS::Record
+
+=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
+
+# the check method should currently be supplied - FS::Record contains some
+# data checking routines
+
+sub check {
+  my $self = shift;
+
+  my $error = 
+    $self->ut_numbern('detailnum')
+    || $self->ut_foreign_key('billpkgnum', 'quotation_pkg', 'quotationpkgnum')
+    || $self->ut_text('detail')
+  ;
+  return $error if $error;
+
+  $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::quotation_pkg>, L<FS::Record>
+
+=cut
+
+1;
+
diff --git a/httemplate/edit/cust_pkg_detail.html b/httemplate/edit/cust_pkg_detail.html
index 5e10706..b1e60da 100644
--- a/httemplate/edit/cust_pkg_detail.html
+++ b/httemplate/edit/cust_pkg_detail.html
@@ -46,7 +46,7 @@
     <TR>
       <TD></TD>
       <TD>
-        <INPUT TYPE="text" NAME="detail<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $_->detail |h %>" rownum="<% $row++ %>" onkeyup = "possiblyAddRow;" >
+        <INPUT TYPE="text" NAME="detail<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $_->detail |h %>" rownum="<% $row++ %>" onkeyup="possiblyAddRow" onchange="possiblyAddRow" >
       </TD>
     </TR>
 
@@ -88,6 +88,7 @@
       detail_input.setAttribute('maxLength', 65);
       detail_input.setAttribute('rownum',   rownum);
       detail_input.onkeyup = possiblyAddRow;
+      detail_input.onchange = possiblyAddRow;
       detail_cell.appendChild(detail_input);
 
     row.appendChild(detail_cell);
diff --git a/httemplate/edit/process/quotation_pkg_detail.html b/httemplate/edit/process/quotation_pkg_detail.html
new file mode 100644
index 0000000..2fc4202
--- /dev/null
+++ b/httemplate/edit/process/quotation_pkg_detail.html
@@ -0,0 +1,45 @@
+% if ( $error ) {
+<% header('Error') %>
+<FONT COLOR="#ff0000"><B><% $error |h %></B></FONT><BR><BR>
+<CENTER><INPUT TYPE="BUTTON" VALUE="OK" onClick="parent.cClick()"></CENTER>
+</BODY></HTML>
+% } else {
+<% header($action) %>
+  <SCRIPT TYPE="text/javascript">
+    window.top.location.reload();
+  </SCRIPT>
+  </BODY></HTML>
+% }
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+die "access denied"
+  unless $curuser->access_right('Generate quotation');
+
+$cgi->param('pkgnum') =~ /^(\d+)$/ or die 'illegal pkgnum';
+my $pkgnum = $1;
+
+my $quotation_pkg = qsearchs({
+  'table'     => 'quotation_pkg',
+  'addl_from' => 'LEFT JOIN quotation USING ( quotationnum )'.
+                 'LEFT JOIN cust_main USING ( custnum )',
+  'hashref'   => { 'quotationpkgnum' => $pkgnum },
+  'extra_sql' => ' AND '. $curuser->agentnums_sql,
+});
+
+my @orig_details = $quotation_pkg->details();
+
+my $action = 'Quotation details'.
+             ( scalar(@orig_details) ? ' changed ' : ' added ' );
+
+my $param = $cgi->Vars;
+my @details = ();
+for ( my $row = 0; exists($param->{"detail$row"}); $row++ ) {
+  push @details, $param->{"detail$row"}
+    if $param->{"detail$row"} =~ /\S/;
+}
+
+my $error = $quotation_pkg->set_details(@details);
+
+</%init>
diff --git a/httemplate/edit/cust_pkg_detail.html b/httemplate/edit/quotation_pkg_detail.html
similarity index 61%
copy from httemplate/edit/cust_pkg_detail.html
copy to httemplate/edit/quotation_pkg_detail.html
index 5e10706..b8f589a 100644
--- a/httemplate/edit/cust_pkg_detail.html
+++ b/httemplate/edit/quotation_pkg_detail.html
@@ -5,22 +5,12 @@
 
 %# <% include('/elements/error.html') %>
 
-<FORM ACTION="process/cust_pkg_detail.html" NAME="DetailForm" ID="DetailForm" METHOD="POST">
+<FORM ACTION="process/quotation_pkg_detail.html" NAME="DetailForm" ID="DetailForm" METHOD="POST">
 
 <INPUT TYPE="hidden" NAME="pkgnum" VALUE="<% $pkgnum %>">
-<INPUT TYPE="hidden" NAME="detailtype" VALUE="<% $detailtype %>">
 
 <TABLE ID="DetailTable" BGCOLOR="#cccccc" BORDER=0 CELLSPACING=1 STYLE="background-color: #cccccc">
 
-% if ( $curuser->option('show_pkgnum') ) {
-
-    <TR>
-      <TD ALIGN="right">Package #</TD>
-      <TD BGCOLOR="#ffffff"><% $pkgnum %></TD>
-    </TR>
-
-% }
-
   <TR>
     <TD ALIGN="right">Package</TD>
     <TD BGCOLOR="#ffffff"><% $part_pkg->pkg %></TD>
@@ -32,12 +22,7 @@
   </TR>
 
   <TR>
-    <TD ALIGN="right">Status</TD>
-    <TD BGCOLOR="#ffffff"><FONT COLOR="#<% $cust_pkg->statuscolor %>"><B><% ucfirst($cust_pkg->status) %></B></FONT></TD>
-  </TR>
-
-  <TR>
-    <TD COLSPAN=2><% ucfirst($name{$detailtype}) %>: </TD>
+    <TD COLSPAN=2>Detail: </TD>
   </TR>
 
 % my $row = 0;
@@ -46,7 +31,7 @@
     <TR>
       <TD></TD>
       <TD>
-        <INPUT TYPE="text" NAME="detail<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $_->detail |h %>" rownum="<% $row++ %>" onkeyup = "possiblyAddRow;" >
+        <INPUT TYPE="text" NAME="detail<% $row %>" SIZE="60" MAXLENGTH="65" VALUE="<% $_ |h %>" rownum="<% $row++ %>" onkeyup="possiblyAddRow" onchange="possiblyAddrow">
       </TD>
     </TR>
 
@@ -60,6 +45,7 @@
 </FORM>
 
 <SCRIPT TYPE="text/javascript">
+% # abject false laziness with edit/cust_pkg_detail.html
 
   var rownum = <% $row %>;
 
@@ -88,6 +74,7 @@
       detail_input.setAttribute('maxLength', 65);
       detail_input.setAttribute('rownum',   rownum);
       detail_input.onkeyup = possiblyAddRow;
+      detail_input.onchange = possiblyAddRow;
       detail_cell.appendChild(detail_input);
 
     row.appendChild(detail_cell);
@@ -104,39 +91,26 @@
 </HTML>
 <%init>
 
-my %access_right = (
-  'I' => 'Edit customer package invoice details', 
-  'C' => 'Edit customer package comments',
-);
-
-my %name = (
-  'I' => 'invoice details',
-  'C' => 'package comments',
-);
-
 my $curuser = $FS::CurrentUser::CurrentUser;
 
-$cgi->param('detailtype') =~ /^(\w)$/ or die 'illegal detailtype';
-my $detailtype = $1;
-
-my $right = $access_right{$detailtype};
 die "access denied"
-  unless $curuser->access_right($right);
+  unless $curuser->access_right('Generate quotation');
 
 $cgi->param('pkgnum') =~ /^(\d+)$/ or die 'illegal pkgnum';
 my $pkgnum = $1;
 
-my $cust_pkg = qsearchs({
-  'table'     => 'cust_pkg',
-  'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
-  'hashref'   => { 'pkgnum' => $pkgnum },
+my $quotation_pkg = qsearchs({
+  'table'     => 'quotation_pkg',
+  'addl_from' => 'LEFT JOIN quotation USING ( quotationnum )'.
+                 'LEFT JOIN cust_main USING ( custnum )',
+  'hashref'   => { 'quotationpkgnum' => $pkgnum },
   'extra_sql' => ' AND '. $curuser->agentnums_sql,
 });
 
-my $part_pkg = $cust_pkg->part_pkg;
+my $part_pkg = $quotation_pkg->part_pkg;
 
-my @details = $cust_pkg->cust_pkg_detail($detailtype);
+my @details = $quotation_pkg->details;
 
-my $title = ( scalar(@details) ? 'Edit ' : 'Add ' ). $name{$detailtype};
+my $title = ( scalar(@details) ? 'Edit ' : 'Add ' ). 'Quotation Details';
 
 </%init>
diff --git a/httemplate/elements/popup_link.html b/httemplate/elements/popup_link.html
index 2b6b187..e9728ac 100644
--- a/httemplate/elements/popup_link.html
+++ b/httemplate/elements/popup_link.html
@@ -18,11 +18,13 @@ Example:
     'height'         => 336,
     'color'          => '#ff0000',
     'closetext'      => 'Go Away',      # the value '' removes the link
+    'title'          => 'Hover Text',
 
     #uncommon opt
     'aname'          => "target", # link NAME= value, useful for #targets
     'target'         => '_parent',
     'style'          => 'css-attribute:value',
+    'html_label'     => '<IMG SRC="something.png">',  # overrides label
   }
   &>
 
@@ -30,6 +32,7 @@ Example:
 % if ($params->{'action'} && $label) {
 <A HREF="javascript:void(0);"
    onClick="<% $onclick |n %>"
+   <% $params->{'title'}  ? 'TITLE="' . $params->{'title'}.  '"' : '' |n %>
    <% $params->{'aname'}  ? 'NAME="'.   $params->{'aname'}.  '"' : '' |n %>
    <% $params->{'target'} ? 'TARGET="'. $params->{'target'}. '"' : '' |n %>
    <% $params->{'style'}  ? 'STYLE="'.  $params->{'style'}.  '"' : '' |n %>
@@ -48,6 +51,7 @@ if (ref($_[0]) eq 'HASH') {
 
 my $label = $params->{'label'};
 $label =~ s/ / /g;
+$label = $params->{'html_label'} || $label;
 my $onclick = include('/elements/popup_link_onclick.html', $params);
 
 </%init>
diff --git a/httemplate/images/Actions-document-edit-icon.png b/httemplate/images/Actions-document-edit-icon.png
new file mode 100644
index 0000000..8bfc329
Binary files /dev/null and b/httemplate/images/Actions-document-edit-icon.png differ
diff --git a/httemplate/view/quotation.html b/httemplate/view/quotation.html
index 67609a1..1862509 100755
--- a/httemplate/view/quotation.html
+++ b/httemplate/view/quotation.html
@@ -91,6 +91,8 @@ my $curuser = $FS::CurrentUser::CurrentUser;
 #die "access denied"
 #  unless $curuser->access_right('View quotations');
 
+my $can_generate_quotation = $curuser->access_right('Generate quotation');
+
 my $quotationnum;
 my($query) = $cgi->keywords;
 if ( $query =~ /^(\d+)$/ ) {
@@ -119,11 +121,23 @@ my $link = "quotationnum=$quotationnum";
 #$link .= ';notice_name='. $notice_name if $notice_name;
 
 my $preref_callback = sub {
-  areyousure_link("${p}misc/delete-quotation_pkg.html?". shift->quotationpkgnum,
+  my $quotation_pkg = shift;
+  $can_generate_quotation ?
+  areyousure_link("${p}misc/delete-quotation_pkg.html?". $quotation_pkg->quotationpkgnum,
                   emt('Are you sure you want to remove this package from the quotation?'),
                   emt('Remove this package'), #tooltip
                   qq(<img src="${p}images/cross.png">), #link
-                 );
+                 ) .
+  include('/elements/popup_link.html',
+    action      => "${p}edit/quotation_pkg_detail.html?pkgnum=" .
+                   $quotation_pkg->quotationpkgnum,
+    html_label  => qq(<IMG SRC="${p}images/Actions-document-edit-icon.png">),
+    title       => emt('Edit quotation details'),
+    actionlabel => emt('Edit quotation details'),
+    color       => '#333399',
+    width       => 763,
+  )
+  : '';
 };
 
 sub areyousure_link {

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

Summary of changes:
 FS/FS/Schema.pm                                    |   18 ++++
 FS/FS/quotation.pm                                 |    3 +-
 FS/FS/quotation_pkg.pm                             |   88 +++++++++++++++++++-
 ...{cust_pkg_detail.pm => quotation_pkg_detail.pm} |   43 ++++------
 httemplate/edit/cust_pkg_detail.html               |    3 +-
 ...t_pkg_detail.html => quotation_pkg_detail.html} |   32 ++-----
 ...t_pkg_detail.html => quotation_pkg_detail.html} |   54 ++++--------
 httemplate/elements/popup_link.html                |    4 +
 httemplate/images/Actions-document-edit-icon.png   |  Bin 0 -> 733 bytes
 httemplate/view/quotation.html                     |   18 +++-
 10 files changed, 169 insertions(+), 94 deletions(-)
 copy FS/FS/{cust_pkg_detail.pm => quotation_pkg_detail.pm} (64%)
 copy httemplate/edit/process/{cust_pkg_detail.html => quotation_pkg_detail.html} (53%)
 copy httemplate/edit/{cust_pkg_detail.html => quotation_pkg_detail.html} (61%)
 create mode 100644 httemplate/images/Actions-document-edit-icon.png




More information about the freeside-commits mailing list