[freeside-commits] branch master updated. 09ae66f29fc7cbd46c13ae1f9361713cbce54153

Ivan ivan at 420.am
Mon Jul 27 02:28:22 PDT 2015


The branch, master has been updated
       via  09ae66f29fc7cbd46c13ae1f9361713cbce54153 (commit)
      from  9aee669886202be7035e6c6049fc71bc99dd3013 (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 09ae66f29fc7cbd46c13ae1f9361713cbce54153
Author: Ivan Kohler <ivan at freeside.biz>
Date:   Mon Jul 27 02:28:20 2015 -0700

    appointment drag and drop, RT#34237

diff --git a/httemplate/elements/menu.html b/httemplate/elements/menu.html
index a01530e..a5fb15b 100644
--- a/httemplate/elements/menu.html
+++ b/httemplate/elements/menu.html
@@ -448,6 +448,26 @@ $report_menu{'Logs'}           = [ \%report_logs, 'System and email logs' ]
 $report_menu{'SQL Query'}      = [ $fsurl.'search/report_sql.html', 'SQL Query']
   if $curuser->access_right('Raw SQL');
 
+tie my %tools_customers, 'Tie::IxHash', ();
+$tools_customers{'Appointments'} = [ $fsurl.'rt/Search/Schedule.html?LengthMin=0', 'View appointment schedule' ]
+  if $curuser->access_right('View appointments');
+$tools_customers{'Attachments'} = [ $fsurl.'browse/cust_attachment.html', 'View customer attachments' ]
+  if !$conf->config('disable_cust_attachment') and $curuser->access_right('View attachments') and $curuser->access_right('Browse attachments');
+$tools_customers{'Customer email settings'} = [ $fsurl.'misc/manage_cust_email.html' ]
+  if $curuser->access_right('Edit customer');
+
+tie my %tools_billing, 'Tie::IxHash', ();
+$tools_billing{'Quick payment entry'} =  [ $fsurl.'misc/batch-cust_pay.html', 'Enter multiple payments in a batch' ]
+  if $curuser->access_right('Post payment batch');
+$tools_billing{'Process payment batches'} = [ $fsurl.'search/pay_batch.cgi?magic=_date;open=1;intransit=1', 'Process credit card and electronic check batches' ]
+  if ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
+     && $curuser->access_right('Process batches');
+$tools_billing{'Download invoice batches'} = [ $fsurl.'search/bill_batch.cgi' ]
+  if $curuser->access_right('Process invoice batches')
+  || $curuser->access_right('Process global invoice batches')
+  || $curuser->access_right('Configuration'); #XXX remove in 2.5
+  #now there's a standalone event#if $conf->exists('invoice_print_pdf');
+
 tie my %tools_importing, 'Tie::IxHash',
   'Customers'            => [ $fsurl.'misc/cust_main-import.cgi', '' ],
   'Package definitions'  => [ $fsurl.'misc/part_pkg-import.html', '' ],
@@ -469,9 +489,17 @@ if ( $conf->config('tax_data_vendor') eq 'cch' ) {
   }
 }
 
-tie my %tools_exporting, 'Tie::IxHash',
-  'Download database dump' => [ $fsurl. 'misc/dump.cgi', '' ],
-;
+tie my %tools_misc, 'Tie::IxHash', ();
+$tools_misc{'Bulk DID Orders'} =  [ $fsurl.'browse/did_order.html', 'View/manage bulk DID orders' ]
+  if $curuser->access_right('Import');
+$tools_misc{'Business card scan'} = [ $fsurl.'edit/prospect_main-upload.html' ]
+  if $curuser->access_right('New prospect');
+$tools_misc{'Time Queue'} =  [ $fsurl.'search/report_timeworked.html', 'View pending support time' ]
+  if $curuser->access_right('Time queue');
+
+#tie my %tools_exporting, 'Tie::IxHash',
+#  'Download database dump' => [ $fsurl. 'misc/dump.cgi', '' ],
+#;
 
 tie my %tools_ticketing_articles, 'Tie::IxHash',
   'Overview' => [ $fsurl.'rt/Articles/index.html', '' ],
@@ -489,38 +517,27 @@ tie my %tools_ticketing, 'Tie::IxHash',
 $tools_ticketing{'Cron Tool'} = [ $fsurl.'rt/Developer/CronTool/', '' ]
   if $conf->exists('rt-crontool');
 
-tie my %tools_menu, 'Tie::IxHash', ();
-$tools_menu{'Quick payment entry'} =  [ $fsurl.'misc/batch-cust_pay.html', 'Enter multiple payments in a batch' ]
-  if $curuser->access_right('Post payment batch');
-$tools_menu{'Process payment batches'} = [ $fsurl.'search/pay_batch.cgi?magic=_date;open=1;intransit=1', 'Process credit card and electronic check batches' ]
-  if ( $conf->exists('batch-enable') || $conf->config('batch-enable_payby') )
-     && $curuser->access_right('Process batches');
-$tools_menu{'Download invoice batches'} = [ $fsurl.'search/bill_batch.cgi' ]
-  if $curuser->access_right('Process invoice batches')
-  || $curuser->access_right('Process global invoice batches')
-  || $curuser->access_right('Configuration'); #XXX remove in 2.5
-  #now there's a standalone event#if $conf->exists('invoice_print_pdf');
-$tools_menu{'Bulk DID Orders'} =  [ $fsurl.'browse/did_order.html', 'View/manage bulk DID orders' ]
-  if $curuser->access_right('Import');
-$tools_menu{'Job Queue'} =  [ $fsurl.'search/queue.html', 'View pending job queue' ]
+tie my %tools_system, 'Tie::IxHash', ();
+$tools_system{'Status'} = [ $fsurl.'view/Status.html', 'System status' ]
+  if $curuser->access_right('Configuration'); # 'View system status');
+$tools_system{'Job Queue'} =  [ $fsurl.'search/queue.html', 'View pending job queue' ]
   if $curuser->access_right('Job queue');
+
+tie my %tools_menu, 'Tie::IxHash', ();
+$tools_menu{'Customers'} = [ \%tools_customers, 'Customer tools' ]
+  if keys %tools_customers;
+$tools_menu{'Billing'} = [ \%tools_billing, 'Payment and invoice tools' ]
+  if keys %tools_billing;
 $tools_menu{'Ticketing'} = [ \%tools_ticketing, 'Ticketing tools' ]
   if $conf->config('ticket_system');
-$tools_menu{'Customer email settings'} = [ $fsurl.'misc/manage_cust_email.html' ]
-  if $curuser->access_right('Edit customer');
-$tools_menu{'Business card scan'} = [ $fsurl.'edit/prospect_main-upload.html' ]
-  if $curuser->access_right('New prospect');
-$tools_menu{'Time Queue'} =  [ $fsurl.'search/report_timeworked.html', 'View pending support time' ]
-  if $curuser->access_right('Time queue');
-$tools_menu{'Attachments'} = [ $fsurl.'browse/cust_attachment.html', 'View customer attachments' ]
-  if !$conf->config('disable_cust_attachment') and $curuser->access_right('View attachments') and $curuser->access_right('Browse attachments');
+$tools_menu{'Miscellaneous'} = [ \%tools_misc, '' ]
+  if keys %tools_misc;
 $tools_menu{'Importing'} =  [ \%tools_importing, 'Import tools' ]
   if $curuser->access_right('Import');
-$tools_menu{'Exporting'} =  [ \%tools_exporting, 'Export tools' ]
-  if $curuser->access_right('Export');
-$tools_menu{'Status'} = [ $fsurl.'view/Status.html', 'System status' ]
-  if $curuser->access_right('Configuration'); # 'View system status');
-
+#$tools_menu{'Exporting'} =  [ \%tools_exporting, 'Export tools' ]
+#  if $curuser->access_right('Export');
+$tools_menu{'System'} = [ \%tools_system, 'System tools' ]
+  if keys %tools_menu;
 
 tie my %config_employees, 'Tie::IxHash',
   'Employees' => [ $fsurl.'browse/access_user.html', 'Setup internal users' ],
diff --git a/rt/share/html/Elements/CalendarSlotSchedule b/rt/share/html/Elements/CalendarSlotSchedule
index ff3e634..045d6e4 100644
--- a/rt/share/html/Elements/CalendarSlotSchedule
+++ b/rt/share/html/Elements/CalendarSlotSchedule
@@ -20,6 +20,10 @@
 %   my $bgcolor = '666666;border-color:#555555';
 %   my $content = '';
 %   my $selectable = 0;
+%   my $draggable_ticketid = 0;
+%   my $draggable_length = 0;
+%   my $droppable = 0;
+%   my $cells = 0;
 %
 %   #white out available times
 %   foreach my $avail ( @{ $schedule{'avail'} } ) {
@@ -44,58 +48,104 @@
 %     $selectable = 0;
 %
 %     if ( $starts >= $tod_row ) { #first row
-%       $content .= ($content?', ':''). $id.
-%                   ': '. FS::sched_avail::pretty_time($starts). '-'.
-%                         FS::sched_avail::pretty_time($due);
+%       $content .= ($content?', ':''). #$id. ': '.
+%                   #false laziness w/xmlhttp-ticket-update.html
+%                   FS::sched_avail::pretty_time($starts). '-'.
+%                   FS::sched_avail::pretty_time($due);
 %                   #'install for custname XX miles away'; #XXX placeholder/more
+%       $draggable_ticketid = $id;
+%       $draggable_length = $due - $starts;
+%
+%       $cells = int( ($due-$starts) / $timestep );
+%       $cells++ if ($due-$starts) % $timestep;
+%       
 %     #} else {
 %     #  $content .= ($content?', ':''). $id;
 %     }
 %   }
+%
+%   my $td_id = 'td_'. $Date->epoch. '_'. $tod_row. '_'. $username;
 
-    <td style="background:#<%$bgcolor%>"
-        class="<% $selectable ? 'weeklyselectable' : 'weekly' %>"
-%#               <%   $is_today     ? 'today'
-%#                  : $is_yesterday ? 'yesterday'
-%#                  : $is_aweekago  ? 'aweekago'
-%#                  : ''
-%#               %>"
+    <td style = "background-color:#<%$bgcolor%>"
+          ID="<% $td_id %>"
+        class = "<% ($selectable && $custnum && $LengthMin) ? 'weeklyselectable' : 'weekly' %>"
+%#                 <%   $is_today     ? 'today'
+%#                    : $is_yesterday ? 'yesterday'
+%#                    : $is_aweekago  ? 'aweekago'
+%#                    : ''
+%#                 %>"
 %     if ( $selectable ) {
 %
-%       #XXX for now, construct a ticket creation URL
-%       # eventually, do much the same, but say "appointment made", show time
-%       # and date, have # options to do things with it? etc.
-%       # then redir back to customer/appointment view i guess
-%
-%       #abstraction is leaking like a sieve... linking back to freeside cust
-%       # (XXX and eventually, package)
-%       my $cust_main = qsearchs('cust_main', { custnum=>$custnum } )
-%         or die "unknown custnum $custnum";
-%       my $Queue = $cust_main->agent->ticketing_queueid || 1; # || $default_queueid;#XXX really, pick pkg_category queue
-%       my $member = "freeside://freeside/cust_main/$custnum";
-%
-%warn       my $Starts = int($tod_row/60). ':'. sprintf('%02d',$tod_row%60). ':00';
-%warn       my $Due    = int(($tod_row+$LengthMin)/60). ':'.
-%                    sprintf('%02d',($tod_row+$LengthMin)%60). ':00';
-%
-%       my $url = $RT::WebPath. '/Ticket/Display.html?id=new'.
-%                 "&Queue=$Queue".
-%                 "&Owner=$username".
-%                 '&Starts='. $Date->strftime('%F').'%20'. $Starts.
-%                 '&Due='.    $Date->strftime('%F').'%20'. $Due.
-%                 '&new-MemberOf='. $member. #XXX uri_escape?
-%                 '&Status=new';
-%                 #'&Requestors='. #XXX Freeside customer requestor(s) (package?
+%       if ( $custnum && $LengthMin ) {
+%
+%         #XXX for now, construct a ticket creation URL
+%         # eventually, do much the same, but say "appointment made", show time
+%         # and date, have # options to do things with it? etc.
+%         # then redir back to customer/appointment view i guess
+%
+%         #abstraction is leaking like a sieve... linking back to freeside cust
+%         # (XXX and eventually, package)
+%         my $cust_main = qsearchs('cust_main', { custnum=>$custnum } )
+%           or die "unknown custnum $custnum";
+%         my $Queue = $cust_main->agent->ticketing_queueid || 1; # || $default_queueid;#XXX really, pick pkg_category queue
+%         my $member = "freeside://freeside/cust_main/$custnum";
+%
+%warn         my $Starts = int($tod_row/60). ':'. sprintf('%02d',$tod_row%60). ':00';
+%warn         my $Due    = int(($tod_row+$LengthMin)/60). ':'.
+%                      sprintf('%02d',($tod_row+$LengthMin)%60). ':00';
+%
+%         my $url = $RT::WebPath. '/Ticket/Display.html?id=new'.
+%                   "&Queue=$Queue".
+%                   "&Owner=$username".
+%                   '&Starts='. $Date->strftime('%F').'%20'. $Starts.
+%                   '&Due='.    $Date->strftime('%F').'%20'. $Due.
+%                   '&new-MemberOf='. $member. #XXX uri_escape?
+%                   '&Status=new';
+%                   #'&Requestors='. #XXX Freeside customer requestor(s) (package?
 
-        onmouseover = "boxon(this);"
-        onmouseout  = "boxoff(this);"
-        title       = "<% 'Make appointment for '.
-                            FS::sched_avail::pretty_time($tod_row). '-'.
-                            FS::sched_avail::pretty_time($tod_row+$LengthMin)
-                      %>"
-        onclick     = "window.location.href = '<% $url %>'"
+          onmouseover = "boxon(this);"
+          onmouseout  = "boxoff(this);"
+          title       = "<% 'Make appointment for '.
+                              FS::sched_avail::pretty_time($tod_row). '-'.
+                              FS::sched_avail::pretty_time($tod_row+$LengthMin)
+                        %>"
+          onclick     = "window.location.href = '<% $url %>'"
+%
+%       } else {
+%         $droppable = 1;
+%       }
+%
 %     }
     ><% $content %></td>
+    <SCRIPT TYPE="text/javascript">
+
+      $('#<% $td_id %>').data('username', "<% $username %>");
+      $('#<% $td_id %>').data('starts',   <% $Date->epoch + $tod_row*60 %>);
+      $('#<% $td_id %>').data('epoch',    <% $Date->epoch %>);
+      $('#<% $td_id %>').data('tod_row',  <% $tod_row %>);
+
+%     if ( $droppable ) {
+        $('#<% $td_id %>').droppable({
+          over: boxon_drop,
+          drop: reschedule_appointment,
+          tolerance: 'pointer'
+        });
+%     }
+
+%     if ( $draggable_ticketid ) {
+        $('#<% $td_id %>').draggable({
+          containment: '.titlebox-content',
+%#          revert:      'invalid',
+          revert: true,
+          revertDuration: 0,
+        });
+        $('#<% $td_id %>').data('ticketid', <% $draggable_ticketid %>);
+        $('#<% $td_id %>').data('length',   <% $draggable_length * 60 %>);
+        $('#<% $td_id %>').data('cells',    <% $cells %>);
+        $('#<% $td_id %>').data('bgcolor',  "#<% $bgcolor %>");
+%     }
+
+    </SCRIPT>
 % }
 <%ONCE>
 my $default_slots = RT->Config->Get('CalendarWeeklySlots') || 5;
diff --git a/rt/share/html/Search/Calendar.html b/rt/share/html/Search/Calendar.html
index 092f6a5..2c19296 100644
--- a/rt/share/html/Search/Calendar.html
+++ b/rt/share/html/Search/Calendar.html
@@ -78,7 +78,7 @@ $DimPast     => 0
     </table>
 % }
 
-<table class="rtxcalendar">
+<table class="<% $WeekDay ? 'rtxweeklycalendar' : 'rtxcalendar' %>">
 
   <thead>
 
diff --git a/rt/share/html/Search/Schedule.html b/rt/share/html/Search/Schedule.html
index 34ba142..be5a140 100644
--- a/rt/share/html/Search/Schedule.html
+++ b/rt/share/html/Search/Schedule.html
@@ -1,14 +1,9 @@
-<& /Elements/Header, Title => 'Schedule' &>
-
-%#init_overlib.html
-%foreach my $file (@files) {
-<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/<%$file%>.js"></SCRIPT>
-%}
-
-<SCRIPT TYPE="text/javascript" SRC="<%$fsurl%>elements/jquery.js"></SCRIPT>
+<& /Elements/Header, Title => 'Schedule', JavaScript => 0 &>
 
 <SCRIPT TYPE="text/javascript">
 
+% if ( $cells ) {
+
   function boxon(what) {
     var $this = $(what);
     for ( var c=0; c < <%$cells%>; c++) {
@@ -48,6 +43,159 @@
     }
   }
 
+
+% }
+
+  var drag_cells = 0;
+  var drag_hi;
+  function boxon_drop(event, ui) {
+    //var $this = $(what);
+    var $this = $(this);
+
+    drag_cells = ui.draggable.data('cells');
+
+    if ( drag_hi ) {
+      boxoff_do(drag_hi);
+    }
+    drag_hi = $this;
+
+    for ( var c=0; c < drag_cells; c++) {
+
+      /* well, its not exactly what i want, would prefer if it could properly
+         mouse in-out, but this sorta helps for now?
+         revisit when everthing else is working */
+/*      $this.effect("highlight", {}, 1500); */
+
+      $this.css('background-color', '#ffffdd');
+      if ( c == 0 ) {
+        $this.css('border-top', '1px double black');
+        $this.css('border-left', '1px double black');
+        $this.css('border-right', '1px solid black');
+      } else if ( c == (drag_cells-1) ) {
+        $this.css('border-left', '1px double black');
+        $this.css('border-right', '1px solid black');
+        $this.css('border-bottom', '1px solid black');
+      } else {
+        $this.css('border-left', '1px double black');
+        $this.css('border-right', '1px solid black');
+      }
+
+      var rownum = $this.parent().prevAll('tr').length;
+      var colnum = $this.prevAll('td').length;
+      $this = $this.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
+    }
+
+
+  }
+
+  function boxoff_do(what) {
+
+    var $this = what;
+
+    for ( var c=0; c < drag_cells; c++) {
+
+      //$this.css('background-color', '');
+      //$this.css('border', ''); //IE8 woes, removes cell borders
+      $this.removeAttr('style'); //slightly "flashy" on cell changes under IE8
+                                 //but at least it doesn't remove cell borders
+
+      var rownum = $this.parent().prevAll('tr').length;
+      var colnum = $this.prevAll('td').length;
+      $this = $this.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
+    }
+  }
+
+  function reschedule_appointment( event, ui ) {
+
+%   #get the ticket number and appointment length (from the draggable object)
+    var ticketid = ui.draggable.data('ticketid');
+    var length   = ui.draggable.data('length');
+    var bgcolor  = ui.draggable.data('bgcolor');
+
+%   #and.. the new date and time, and username (from the droppable object)
+    var starts   = $(this).data('starts');
+    var username = $(this).data('username');
+
+    var due = parseInt(starts) + parseInt(length);
+
+    var n_epoch        = $(this).data('epoch');
+    var n_st_tod_row   = $(this).data('tod_row');
+
+    var draggable = ui.draggable;
+    var droppable = $(this);
+    draggable.effect( "transfer", { to: droppable }, 1000 );
+
+%   #tell the backend to reschedule it
+    var url = "<% popurl(3) %>misc/xmlhttp-ticket-update.html?" +
+              "id=" + ticketid + ";starts=" + starts + ";due=" + due +
+              ";username=" + username;
+
+    $.getJSON( url, function( data ) {
+      if ( data.error && data.error.length ) {
+%       #error?  "that shouldn't happen" but should display 
+        alert(data.error);
+%       #XX and should revert the dragable...
+      } else {
+
+        //draggable.effect( "transfer", { to: droppable }, 1000 );
+
+        var label = data.sched_label;
+
+%       #remove the old appointment entirely
+        var epoch        = ui.draggable.data('epoch');
+        var st_tod_row   = ui.draggable.data('tod_row');
+        var old_username = ui.draggable.data('username');
+        var cells        = ui.draggable.data('cells');
+        for ( var c=0; c < cells; c++) {
+          var tod_row = parseInt(st_tod_row) + (c * <%$timestep%>);
+          var td_id = 'td_' + epoch +
+                      '_' + String( tod_row ) +
+                      '_' + old_username;
+          $('#'+td_id).css('background-color', '#FFFFFF');
+          $('#'+td_id).text('');
+%         #(and make those boxes droppable)
+          $('#'+td_id).droppable({
+            over: boxon_drop,
+            drop: reschedule_appointment,
+            tolerance: 'pointer'
+          });
+        }
+
+%       #maybe use that animation which shows the box from point A to B
+
+        if ( drag_hi ) {
+          boxoff_do(drag_hi);
+        }
+        for ( var d=0; d < cells; d++) {
+          var n_tod_row = parseInt(n_st_tod_row) + (d * <%$timestep%>);
+          var n_td_id = 'td_' + n_epoch +
+                        '_' + String( n_tod_row ) +
+                        '_' + username;
+          $('#'+n_td_id).css('background-color', bgcolor);
+%         #remove their droppable
+          $('#'+n_td_id).droppable('destroy');
+          if ( d == 0 ) {
+            $('#'+n_td_id).text(label);
+%           #(and make the top draggable, so we could do it all over again)
+            $('#'+n_td_id).draggable({
+              containment: '.titlebox-content',
+%#              revert:      'invalid',
+              revert: true,
+              revertDuration: 0,
+            });
+            $('#'+n_td_id).data('ticketid', ticketid );
+            $('#'+n_td_id).data('length',   length );
+            $('#'+n_td_id).data('cells',    cells );
+            $('#'+n_td_id).data('bgcolor',  bgcolor );
+          }
+        }
+
+      }
+
+    });
+
+  }
+
 </SCRIPT>
 
 <& /Search/Calendar.html,
@@ -86,9 +234,13 @@ if ( ref($ARGS{username}) ) {
 } elsif ( $ARGS{username} ) {
   @usernames = ( $ARGS{username} );
 } else {
-  #XXX shouldn't even get offered the link in the first place rather than perl
-  # barf, but this is better than erroring out later or empty @username
-  die "Can't schedule an appointment - no employees are configured as installers";
+  #look them up ourslves... again, more FS abstraction-leaking, but 
+  # we want to link to the schedule view, and better than doing this every
+  # menu render
+  use FS::Record qw( qsearch );
+  use FS::sched_item;
+  my @sched_item = qsearch('sched_item', { 'disabled' => '', });
+  @usernames = map $_->access_user->username, @sched_item;
 }
 
 ( my $LengthMin = $ARGS{LengthMin} ) =~ /^\d+$/ or die 'non-numeric LengthMin';
diff --git a/rt/share/static/css/calendar.css b/rt/share/static/css/calendar.css
index a91917c..566f969 100644
--- a/rt/share/static/css/calendar.css
+++ b/rt/share/static/css/calendar.css
@@ -201,3 +201,7 @@ table.rtxweeklycalendar td.labels {
     border-bottom: 1px solid #eeeeee;
 }
 
+.ui-effects-transfer {
+  border: 1px solid black;
+}
+

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

Summary of changes:
 httemplate/elements/menu.html               |   77 +++++++-----
 rt/share/html/Elements/CalendarSlotSchedule |  132 +++++++++++++-------
 rt/share/html/Search/Calendar.html          |    2 +-
 rt/share/html/Search/Schedule.html          |  174 +++++++++++++++++++++++++--
 rt/share/static/css/calendar.css            |    4 +
 5 files changed, 306 insertions(+), 83 deletions(-)




More information about the freeside-commits mailing list