Merge branch 'master' of git.freeside.biz:/home/git/freeside
authorIvan Kohler <ivan@freeside.biz>
Mon, 2 Jul 2012 21:24:21 +0000 (14:24 -0700)
committerIvan Kohler <ivan@freeside.biz>
Mon, 2 Jul 2012 21:24:21 +0000 (14:24 -0700)
12 files changed:
FS/FS/Mason.pm
FS/FS/Quotable_Mixin.pm [new file with mode: 0644]
FS/FS/quotation.pm [new file with mode: 0644]
FS/FS/quotation_pkg.pm [new file with mode: 0644]
FS/FS/quotation_pkg_discount.pm [new file with mode: 0644]
FS/t/Quotable_Mixin.t [new file with mode: 0644]
FS/t/quotation.t [new file with mode: 0644]
FS/t/quotation_pkg.t [new file with mode: 0644]
FS/t/quotation_pkg_discount.t [new file with mode: 0644]
httemplate/edit/process/quotation.html [new file with mode: 0644]
httemplate/edit/quotation.html [new file with mode: 0644]
httemplate/view/quotation.html [new file with mode: 0755]

index 16c9afd..9bd0cc3 100644 (file)
@@ -309,6 +309,9 @@ if ( -e $addl_handler_use_file ) {
   use FS::contact_class;
   use FS::part_svc_class;
   use FS::ftp_target;
+  use FS::quotation;
+  use FS::quotation_pkg;
+  use FS::quotation_pkg_discount;
   # Sammath Naur
 
   if ( $FS::Mason::addl_handler_use ) {
diff --git a/FS/FS/Quotable_Mixin.pm b/FS/FS/Quotable_Mixin.pm
new file mode 100644 (file)
index 0000000..dfd3ddd
--- /dev/null
@@ -0,0 +1,13 @@
+package FS::Quotable_Mixin;
+
+use strict;
+use FS::Record qw( qsearch ); #qsearchs );
+use FS::quotation;
+
+sub quotation {
+  my $self = shift;
+  my $pk = $self->primary_key;
+  qsearch('quotation', { $pk => $self->$pk() } );
+}
+
+1;
diff --git a/FS/FS/quotation.pm b/FS/FS/quotation.pm
new file mode 100644 (file)
index 0000000..4202335
--- /dev/null
@@ -0,0 +1,148 @@
+package FS::quotation;
+
+use strict;
+use base qw( FS::otaker_Mixin FS::Record );
+use FS::Record; # qw( qsearch qsearchs );
+use FS::cust_main;
+use FS::prospect_main;
+
+=head1 NAME
+
+FS::quotation - Object methods for quotation records
+
+=head1 SYNOPSIS
+
+  use FS::quotation;
+
+  $record = new FS::quotation \%hash;
+  $record = new FS::quotation { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::quotation object represents a quotation.  FS::quotation inherits from
+FS::Record.  The following fields are currently supported:
+
+=over 4
+
+=item quotationnum
+
+primary key
+
+=item prospectnum
+
+prospectnum
+
+=item custnum
+
+custnum
+
+=item _date
+
+_date
+
+=item disabled
+
+disabled
+
+=item usernum
+
+usernum
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new quotation.  To add the quotation 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 { 'quotation'; }
+
+=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 quotation.  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('quotationnum')
+    || $self->ut_foreign_keyn('prospectnum', 'prospect_main', 'prospectnum' )
+    || $self->ut_foreign_keyn('custnum', 'cust_main', 'custnum' )
+    || $self->ut_numbern('_date')
+    || $self->ut_enum('disabled', [ '', 'Y' ])
+    || $self->ut_numbern('usernum')
+  ;
+  return $error if $error;
+
+  $self->_date(time) unless $self->_date;
+
+  #XXX set usernum
+
+  $self->SUPER::check;
+}
+
+=item prospect_main
+
+=cut
+
+sub prospect_main {
+  my $self = shift;
+  qsearchs('prospect_main', { 'prospectnum' => $self->prospectnum } );
+}
+
+=item cust_main
+
+=cut
+
+sub cust_main {
+  my $self = shift;
+  qsearchs('cust_main', { 'custnum' => $self->custnum } );
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/quotation_pkg.pm b/FS/FS/quotation_pkg.pm
new file mode 100644 (file)
index 0000000..48d5906
--- /dev/null
@@ -0,0 +1,133 @@
+package FS::quotation_pkg;
+
+use strict;
+use base qw( FS::Record );
+use FS::Record; # qw( qsearch qsearchs );
+use FS::part_pkg;
+use FS::cust_location;
+
+=head1 NAME
+
+FS::quotation_pkg - Object methods for quotation_pkg records
+
+=head1 SYNOPSIS
+
+  use FS::quotation_pkg;
+
+  $record = new FS::quotation_pkg \%hash;
+  $record = new FS::quotation_pkg { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::quotation_pkg object represents a quotation package.
+FS::quotation_pkg inherits from FS::Record.  The following fields are currently
+supported:
+
+=over 4
+
+=item quotationpkgnum
+
+primary key
+
+=item pkgpart
+
+pkgpart
+
+=item locationnum
+
+locationnum
+
+=item start_date
+
+start_date
+
+=item contract_end
+
+contract_end
+
+=item quantity
+
+quantity
+
+=item waive_setup
+
+waive_setup
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new quotation package.  To add the quotation package 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 { 'quotation_pkg'; }
+
+=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 quotation package.  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('quotationpkgnum')
+    || $self->ut_foreign_key('pkgpart', 'part_pkg', 'pkgpart' )
+    || $self->ut_foreign_keyn('locationnum', 'cust_location', 'locationnum' )
+    || $self->ut_numbern('start_date')
+    || $self->ut_numbern('contract_end')
+    || $self->ut_numbern('quantity')
+    || $self->ut_enum('waive_setup', [ '', 'Y'] )
+  ;
+  return $error if $error;
+
+  $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/FS/quotation_pkg_discount.pm b/FS/FS/quotation_pkg_discount.pm
new file mode 100644 (file)
index 0000000..34e13a6
--- /dev/null
@@ -0,0 +1,128 @@
+package FS::quotation_pkg_discount;
+
+use strict;
+use base qw( FS::Record );
+use FS::Record qw( qsearch qsearchs );
+
+=head1 NAME
+
+FS::quotation_pkg_discount - Object methods for quotation_pkg_discount records
+
+=head1 SYNOPSIS
+
+  use FS::quotation_pkg_discount;
+
+  $record = new FS::quotation_pkg_discount \%hash;
+  $record = new FS::quotation_pkg_discount { 'column' => 'value' };
+
+  $error = $record->insert;
+
+  $error = $new_record->replace($old_record);
+
+  $error = $record->delete;
+
+  $error = $record->check;
+
+=head1 DESCRIPTION
+
+An FS::quotation_pkg_discount object represents a quotation package discount.
+FS::quotation_pkg_discount inherits from FS::Record.  The following fields are
+currently supported:
+
+=over 4
+
+=item quotationpkgdiscountnum
+
+primary key
+
+=item quotationpkgnum
+
+quotationpkgnum
+
+=item discountnum
+
+discountnum
+
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item new HASHREF
+
+Creates a new quotation package discount.  To add the quotation package
+discount 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_discount'; }
+
+=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 quotation package discount.
+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('quotationpkgdiscountnum')
+    || $self->ut_foreign_key('quotationpkgnum', 'quotation_pkg', 'quotationpkgnum' )
+    || $self->ut_foreign_key('discountnum', 'discount', 'discountnum' )
+  ;
+  return $error if $error;
+
+  $self->SUPER::check;
+}
+
+=back
+
+=head1 BUGS
+
+=head1 SEE ALSO
+
+L<FS::Record>, schema.html from the base documentation.
+
+=cut
+
+1;
+
diff --git a/FS/t/Quotable_Mixin.t b/FS/t/Quotable_Mixin.t
new file mode 100644 (file)
index 0000000..cb0a561
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::Quotable_Mixin;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/quotation.t b/FS/t/quotation.t
new file mode 100644 (file)
index 0000000..effcac6
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::quotation;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/quotation_pkg.t b/FS/t/quotation_pkg.t
new file mode 100644 (file)
index 0000000..5164c7e
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::quotation_pkg;
+$loaded=1;
+print "ok 1\n";
diff --git a/FS/t/quotation_pkg_discount.t b/FS/t/quotation_pkg_discount.t
new file mode 100644 (file)
index 0000000..a1c5f53
--- /dev/null
@@ -0,0 +1,5 @@
+BEGIN { $| = 1; print "1..1\n" }
+END {print "not ok 1\n" unless $loaded;}
+use FS::quotation_pkg_discount;
+$loaded=1;
+print "ok 1\n";
diff --git a/httemplate/edit/process/quotation.html b/httemplate/edit/process/quotation.html
new file mode 100644 (file)
index 0000000..7671c36
--- /dev/null
@@ -0,0 +1,11 @@
+<% include( 'elements/process.html',
+               'table'       => 'quotation',
+               'redirect'    => $p.'view/quotation.html?',
+           )
+%>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Generate quotation');
+
+</%init>
diff --git a/httemplate/edit/quotation.html b/httemplate/edit/quotation.html
new file mode 100644 (file)
index 0000000..f706425
--- /dev/null
@@ -0,0 +1,15 @@
+<% include( 'elements/edit.html',
+                 'name'   => 'Quotation',
+                 'table'  => 'quotation',
+                 'labels' => { 
+                               'quotationnum' => 'Quotation number',
+                             },
+                 #XXX some way to disable the "view all"
+           )
+%>
+<%init>
+
+die "access denied"
+  unless $FS::CurrentUser::CurrentUser->access_right('Generate quotation');
+
+</%init>
diff --git a/httemplate/view/quotation.html b/httemplate/view/quotation.html
new file mode 100755 (executable)
index 0000000..2c2c6b7
--- /dev/null
@@ -0,0 +1,81 @@
+<& /elements/header.html, mt('Quotation View'), $menubar &>
+
+%#XXX link to order...
+
+<%doc>
+
+XXX resending quotations
+
+% if ( $curuser->access_right('Resend invoices') ) {
+
+    <A HREF="<% $p %>misc/send-invoice.cgi?method=print;<% $link %>"><% mt('Re-print this invoice') |h %></A>
+
+%   if ( grep { $_ ne 'POST' } $cust_bill->cust_main->invoicing_list ) { 
+        | <A HREF="<% $p %>misc/send-invoice.cgi?method=email;<% $link %>"><% mt('Re-email this invoice') |h %></A>
+%   } 
+
+%   if ( $conf->exists('hylafax') && length($cust_bill->cust_main->fax) ) { 
+        | <A HREF="<% $p %>misc/send-invoice.cgi?method=fax;<% $link %>"><% mt('Re-fax this invoice') |h %></A>
+%   } 
+
+    <BR><BR>
+
+% } 
+
+XXX view typset quotation
+
+% if ( $conf->exists('invoice_latex') ) { 
+
+  <A HREF="<% $p %>view/cust_bill-pdf.cgi?<% $link %>"><% mt('View typeset invoice PDF') |h %></A>
+  <BR><BR>
+% } 
+
+XXX actually show the quotation
+
+% if ( $conf->exists('invoice_html') ) { 
+  <% join('', $cust_bill->print_html(\%opt) ) %>
+% } else { 
+  <PRE><% join('', $cust_bill->print_text(\%opt) ) %></PRE>
+% } 
+
+</%doc>
+
+<& /elements/footer.html &>
+<%init>
+
+my $curuser = $FS::CurrentUser::CurrentUser;
+
+#die "access denied"
+#  unless $curuser->access_right('View quotations');
+
+my $quotationnum;
+my($query) = $cgi->keywords;
+if ( $query =~ /^(\d+)$/ ) {
+  $quotationnum = $1;
+} else {
+  $quotationnum = $cgi->param('quotationnum');
+}
+
+#my $conf = new FS::Conf;
+
+my $quotation = qsearchs({
+  'select'    => 'quotation.*',
+  'table'     => 'quotation',
+  #'addl_from' => 'LEFT JOIN cust_main USING ( custnum )',
+  'hashref'   => { 'quotationnum' => $quotationnum },
+  #'extra_sql' => ' AND '. $curuser->agentnums_sql,
+});
+die "Quotation #$quotationnum not found!" unless $quotation;
+
+if ( my $custnum = $quotation->custnum ) {
+  my $display_custnum = $quotation->cust_main->display_custnum;
+  $menubar = menubar(
+    emt("View this customer (#[_1])",$display_custnum) => "${p}view/cust_main.cgi?$custnum",
+  );
+} elsif ( my $prospectnum = $quotation->prospectnum ) {
+  $menubar = menubar(
+    emt("View this prospect (#[_1])",$prospectnum) => "${p}view/prospect_main.html?$prospectnum",
+  );
+}
+
+</%init>