Merge branch 'master' of vc.linet-services.de:public/lx-office-erp
authorSven Schöling <s.schoeling@linet-services.de>
Tue, 11 Oct 2011 13:13:04 +0000 (15:13 +0200)
committerSven Schöling <s.schoeling@linet-services.de>
Tue, 11 Oct 2011 13:13:04 +0000 (15:13 +0200)
22 files changed:
SL/CVar.pm
SL/Controller/Base.pm
SL/Controller/Customer.pm [new file with mode: 0644]
SL/Controller/Part.pm [new file with mode: 0644]
SL/DB/Customer.pm
SL/DB/Helper/Mappings.pm
SL/DB/Manager/Part.pm
SL/DB/MetaSetup/Invoice.pm
SL/DB/MetaSetup/Order.pm
SL/Form.pm
SL/Template/Plugin/JSON.pm [new file with mode: 0644]
SL/Template/Plugin/L.pm
SL/Template/Simple.pm
bin/mozilla/amcvar.pl
sql/Pg-upgrade2/oe_customer_vendor_fkeys.sql [new file with mode: 0644]
templates/webpages/amcvar/render_inputs.html
templates/webpages/amcvar/render_inputs_block.html
templates/webpages/amcvar/search_filter.html
templates/webpages/ct/ajax_autocomplete2.html [new file with mode: 0644]
templates/webpages/ct/testpage.html [new file with mode: 0644]
templates/webpages/part/ajax_autocomplete.html [new file with mode: 0644]
templates/webpages/part/testpage.html [new file with mode: 0644]

index 779bfc3..8b3cd8e 100644 (file)
@@ -240,6 +240,7 @@ sub get_custom_variables {
       $cvar->{value} = $cvar->{type} eq 'date'      ? $act_var->{date_value}
                      : $cvar->{type} eq 'timestamp' ? $act_var->{timestamp_value}
                      : $cvar->{type} eq 'number'    ? $act_var->{number_value}
+                     : $cvar->{type} eq 'customer'  ? $act_var->{number_value}
                      : $cvar->{type} eq 'bool'      ? $act_var->{bool_value}
                      :                                $act_var->{text_value};
       $cvar->{valid} = $valid;
@@ -273,6 +274,9 @@ sub get_custom_variables {
 
     if ($cvar->{type} eq 'number') {
       $cvar->{value} = $form->format_amount($myconfig, $cvar->{value} * 1, $cvar->{precision});
+    } elsif ($cvar->{type} eq 'customer') {
+      require SL::DB::Customer;
+      $cvar->{value} = SL::DB::Manager::Customer->find_by(id => $cvar->{value} * 1);
     }
   }
 
@@ -334,6 +338,8 @@ sub save_custom_variables {
 
     } elsif ($config->{type} eq 'bool') {
       push @values, $value ? 't' : 'f', undef, undef, undef;
+    } elsif ($config->{type} eq 'customer') {
+      push @values, undef, undef, undef, $value * 1;
     }
 
     do_statement($form, $sth, $query, @values);
@@ -489,6 +495,11 @@ sub build_filter_query {
 
       $not = 'NOT' if ($params{filter}->{$name} eq 'no');
       push @sub_where,  qq|COALESCE(cvar.bool_value, false) = TRUE|;
+    } elsif ($config->{type} eq 'customer') {
+      next unless $params{filter}->{$name};
+
+      push @sub_where, qq|cvar.number_value * 1 IN (SELECT id FROM customer WHERE name ILIKE ?)|;
+      push @sub_values, "%$params{filter}->{$name}%";
     }
 
     if (@sub_where) {
@@ -562,6 +573,7 @@ sub add_custom_variables_to_report {
           $cfg->{type} eq 'date'      ? $ref->{date_value}
         : $cfg->{type} eq 'timestamp' ? $ref->{timestamp_value}
         : $cfg->{type} eq 'number'    ? $form->format_amount($myconfig, $ref->{number_value} * 1, $cfg->{precision})
+        : $cfg->{type} eq 'customer'  ? SL::DB::Manager::Customer->find_by(id => 1* $ref->{number_value})->name
         : $cfg->{type} eq 'bool'      ? ($ref->{bool_value} ? $locale->text('Yes') : $locale->text('No'))
         :                               $ref->{text_value};
     }
index f8eee88..42bf6bd 100644 (file)
@@ -45,6 +45,9 @@ sub render {
   if ($options->{inline}) {
     $source = \$template;
 
+  } elsif($options->{raw}) {
+    $source =  $template;
+
   } else {
     $source = "templates/webpages/${template}." . $options->{type};
     croak "Template file ${source} not found" unless -f $source;
@@ -77,8 +80,12 @@ sub render {
                );
 
   my $output;
-  my $parser = $self->_template_obj;
-  $parser->process($source, \%params, \$output) || croak $parser->error;
+  if (!$options->{raw}) {
+    my $parser = $self->_template_obj;
+    $parser->process($source, \%params, \$output) || croak $parser->error;
+  } else {
+    $output = $$source;
+  }
 
   print $output unless $options->{inline} || $options->{no_output};
 
@@ -322,6 +329,10 @@ containing the template code to interprete. Additionally the output
 will not be sent to the browser. Instead it is only returned to the
 caller.
 
+If C<< $options->{raw}>> is trueish, the function will treat the input as
+already parsed, and will not filter the input through Template. Unlike
+C<inline>, the input is taked as a reference.
+
 If C<< $options->{inline} >> is falsish then C<$template> is
 interpreted as the name of a template file. It is prefixed with
 "templates/webpages/" and postfixed with a file extension based on
diff --git a/SL/Controller/Customer.pm b/SL/Controller/Customer.pm
new file mode 100644 (file)
index 0000000..4358080
--- /dev/null
@@ -0,0 +1,27 @@
+package SL::Controller::Customer;
+
+use strict;
+use parent qw(SL::Controller::Base);
+
+use SL::DB::Customer;
+
+# safety
+__PACKAGE__->run_before(sub { $::auth->assert('customer_vendor_edit') });
+
+sub action_ajax_autocomplete {
+  my ($self, %params) = @_;
+
+  my $limit  = $::form->{limit}  || 20;
+  my $type   = $::form->{type} || {};
+  my $query  = { ilike => "%$::form->{term}%" };
+  my @filter;
+  push @filter, ($::form->{column})
+    ? ($::form->{column} => $query)
+    : (or => [ customernumber => $query, name => $query ]);
+
+  $self->{customers} = SL::DB::Manager::Customer->get_all(query => [ @filter ], limit => $limit);
+  $self->{value} = $::form->{column} || 'name';
+
+  $self->render('ct/ajax_autocomplete2', { no_layout => 1 });
+}
+
diff --git a/SL/Controller/Part.pm b/SL/Controller/Part.pm
new file mode 100644 (file)
index 0000000..36859e7
--- /dev/null
@@ -0,0 +1,30 @@
+package SL::Controller::Part;
+
+use strict;
+use parent qw(SL::Controller::Base);
+
+use SL::DB::Part;
+
+# safety
+__PACKAGE__->run_before(sub { $::auth->assert('part_service_assembly_edit') });
+
+sub action_ajax_autocomplete {
+  my ($self, %params) = @_;
+
+  my $limit  = $::form->{limit}  || 20;
+  my $type   = $::form->{type} || {};
+  my $query  = { ilike => "%$::form->{term}%" };
+  my @filter;
+  push @filter, SL::DB::Manager::Part->type_filter($type);
+  push @filter, ($::form->{column})
+    ? ($::form->{column} => $query)
+    : (or => [ partnumber => $query, description => $query ]);
+
+  $self->{parts} = SL::DB::Manager::Part->get_all(query => [ @filter ], limit => $limit);
+  $self->{value} = $::form->{column} || 'description';
+
+  $self->render('part/ajax_autocomplete', { no_layout => 1 });
+}
+
+
+1;
index 0adffbd..f9f8901 100644 (file)
@@ -50,4 +50,10 @@ sub short_address {
   return join ', ', grep { $_ } $self->street, $self->zipcode, $self->city;
 }
 
+sub displayable_name {
+  my $self = shift;
+
+  return join ' ', grep $_, $self->customernumber, $self->name;
+}
+
 1;
index b178271..8382f1f 100644 (file)
@@ -105,6 +105,7 @@ my %lxoffice_package_names = (
   translation_payment_terms      => 'translation_payment_term',
   units                          => 'unit',
   units_language                 => 'units_language',
+  vendor                         => 'vendor',
   vendortax                      => 'vendor_tax',
 );
 
index e6aca1f..50219cb 100644 (file)
@@ -7,32 +7,36 @@ use base qw(SL::DB::Helper::Manager);
 
 use Carp;
 use SL::DBUtils;
+use SL::MoreCommon qw(listify);
 
 sub object_class { 'SL::DB::Part' }
 
 __PACKAGE__->make_manager_methods;
 
 sub type_filter {
-  my $class = shift;
-  my $type  = lc(shift || '');
-
-  if ($type =~ m/^part/) {
-    return (and => [ or                    => [ assembly => 0, assembly => undef ],
-                     '!inventory_accno_id' => 0,
-                     '!inventory_accno_id' => undef,
-                   ]);
-
-  } elsif ($type =~ m/^service/) {
-    return (and => [ or => [ assembly           => 0, assembly           => undef ],
-                     or => [ inventory_accno_id => 0, inventory_accno_id => undef ],
-                   ]);
-
-  } elsif ($type =~ m/^assembl/) {
-    return (assembly => 1);
-
+  my ($class, $type) = @_;
+
+  return () unless $type;
+
+  my @types = listify($type);
+  my @filter;
+
+  for my $type (@types) {
+    if ($type =~ m/^part/) {
+      push @filter, (and => [ or                    => [ assembly => 0, assembly => undef ],
+                       '!inventory_accno_id' => 0,
+                       '!inventory_accno_id' => undef,
+                     ]);
+    } elsif ($type =~ m/^service/) {
+      push @filter, (and => [ or => [ assembly           => 0, assembly           => undef ],
+                       or => [ inventory_accno_id => 0, inventory_accno_id => undef ],
+                     ]);
+    } elsif ($type =~ m/^assembl/) {
+      push @filter, (assembly => 1);
+    }
   }
 
-  return ();
+  return @filter ? (or => \@filter) : ();
 }
 
 sub get_ordered_qty {
index be3466d..d300a7e 100644 (file)
@@ -10,51 +10,52 @@ __PACKAGE__->meta->setup(
   table   => 'ar',
 
   columns => [
-    id                      => { type => 'integer', not_null => 1, sequence => 'glid' },
-    invnumber               => { type => 'text', not_null => 1 },
-    transdate               => { type => 'date', default => 'now' },
-    gldate                  => { type => 'date', default => 'now' },
-    customer_id             => { type => 'integer' },
-    taxincluded             => { type => 'boolean' },
-    amount                  => { type => 'numeric', precision => 5, scale => 15 },
-    netamount               => { type => 'numeric', precision => 5, scale => 15 },
-    paid                    => { type => 'numeric', precision => 5, scale => 15 },
-    datepaid                => { type => 'date' },
-    duedate                 => { type => 'date' },
-    deliverydate            => { type => 'date' },
-    invoice                 => { type => 'boolean', default => 'false' },
-    shippingpoint           => { type => 'text' },
-    terms                   => { type => 'integer', default => '0' },
-    notes                   => { type => 'text' },
-    curr                    => { type => 'character', length => 3 },
-    ordnumber               => { type => 'text' },
-    employee_id             => { type => 'integer' },
-    quonumber               => { type => 'text' },
-    cusordnumber            => { type => 'text' },
-    intnotes                => { type => 'text' },
-    department_id           => { type => 'integer', default => '0' },
-    shipvia                 => { type => 'text' },
-    itime                   => { type => 'timestamp', default => 'now()' },
-    mtime                   => { type => 'timestamp' },
-    cp_id                   => { type => 'integer' },
-    language_id             => { type => 'integer' },
-    payment_id              => { type => 'integer' },
-    delivery_customer_id    => { type => 'integer' },
-    delivery_vendor_id      => { type => 'integer' },
-    storno                  => { type => 'boolean', default => 'false' },
-    taxzone_id              => { type => 'integer' },
-    shipto_id               => { type => 'integer' },
-    type                    => { type => 'text' },
-    dunning_config_id       => { type => 'integer' },
-    orddate                 => { type => 'date' },
-    quodate                 => { type => 'date' },
-    globalproject_id        => { type => 'integer' },
-    salesman_id             => { type => 'integer' },
-    transaction_description => { type => 'text' },
-    storno_id               => { type => 'integer' },
-    marge_total             => { type => 'numeric', precision => 5, scale => 15 },
-    marge_percent           => { type => 'numeric', precision => 5, scale => 15 },
-    donumber                => { type => 'text' },
+    id                        => { type => 'integer', not_null => 1, sequence => 'glid' },
+    invnumber                 => { type => 'text', not_null => 1 },
+    transdate                 => { type => 'date', default => 'now' },
+    gldate                    => { type => 'date', default => 'now' },
+    customer_id               => { type => 'integer' },
+    taxincluded               => { type => 'boolean' },
+    amount                    => { type => 'numeric', precision => 5, scale => 15 },
+    netamount                 => { type => 'numeric', precision => 5, scale => 15 },
+    paid                      => { type => 'numeric', precision => 5, scale => 15 },
+    datepaid                  => { type => 'date' },
+    duedate                   => { type => 'date' },
+    deliverydate              => { type => 'date' },
+    invoice                   => { type => 'boolean', default => 'false' },
+    shippingpoint             => { type => 'text' },
+    terms                     => { type => 'integer', default => '0' },
+    notes                     => { type => 'text' },
+    curr                      => { type => 'character', length => 3 },
+    ordnumber                 => { type => 'text' },
+    employee_id               => { type => 'integer' },
+    quonumber                 => { type => 'text' },
+    cusordnumber              => { type => 'text' },
+    intnotes                  => { type => 'text' },
+    department_id             => { type => 'integer', default => '0' },
+    shipvia                   => { type => 'text' },
+    itime                     => { type => 'timestamp', default => 'now()' },
+    mtime                     => { type => 'timestamp' },
+    cp_id                     => { type => 'integer' },
+    language_id               => { type => 'integer' },
+    payment_id                => { type => 'integer' },
+    delivery_customer_id      => { type => 'integer' },
+    delivery_vendor_id        => { type => 'integer' },
+    storno                    => { type => 'boolean', default => 'false' },
+    taxzone_id                => { type => 'integer' },
+    shipto_id                 => { type => 'integer' },
+    type                      => { type => 'text' },
+    dunning_config_id         => { type => 'integer' },
+    orddate                   => { type => 'date' },
+    quodate                   => { type => 'date' },
+    globalproject_id          => { type => 'integer' },
+    salesman_id               => { type => 'integer' },
+    transaction_description   => { type => 'text' },
+    storno_id                 => { type => 'integer' },
+    marge_total               => { type => 'numeric', precision => 5, scale => 15 },
+    marge_percent             => { type => 'numeric', precision => 5, scale => 15 },
+    donumber                  => { type => 'text' },
+    invnumber_for_credit_note => { type => 'text' },
   ],
 
   primary_key_columns => [ 'id' ],
index e616aa3..3595026 100644 (file)
@@ -53,6 +53,11 @@ __PACKAGE__->meta->setup(
   allow_inline_column_values => 1,
 
   foreign_keys => [
+    customer => {
+      class       => 'SL::DB::Customer',
+      key_columns => { customer_id => 'id' },
+    },
+
     employee => {
       class       => 'SL::DB::Employee',
       key_columns => { employee_id => 'id' },
@@ -67,6 +72,11 @@ __PACKAGE__->meta->setup(
       class       => 'SL::DB::Employee',
       key_columns => { salesman_id => 'id' },
     },
+
+    vendor => {
+      class       => 'SL::DB::Vendor',
+      key_columns => { vendor_id => 'id' },
+    },
   ],
 );
 
index a23bb43..b21f94c 100644 (file)
@@ -701,7 +701,9 @@ sub header {
                 '<script type="text/javascript" src="js/jscalendar/calendar.js"></script>',
                 '<script type="text/javascript" src="js/jscalendar/lang/calendar-de.js"></script>',
                 '<script type="text/javascript" src="js/jscalendar/calendar-setup.js"></script>',
-                '<script type="text/javascript" src="js/part_selection.js"></script>';
+                '<script type="text/javascript" src="js/part_selection.js"></script>',
+                '<script type="text/javascript" src="js/jquery-ui.js"></script>',
+                '<style "type=text/css">@import url("css/ui-lightness/jquery-ui-1.8.12.custom.css")</style>';
   push @header, $self->{javascript} if $self->{javascript};
   push @header, map { $_->show_javascript } @{ $self->{AJAX} || [] };
   push @header, "<script type='text/javascript'>function fokus(){ document.$self->{fokus}.focus(); }</script>" if $self->{fokus};
diff --git a/SL/Template/Plugin/JSON.pm b/SL/Template/Plugin/JSON.pm
new file mode 100644 (file)
index 0000000..3709d8f
--- /dev/null
@@ -0,0 +1,51 @@
+package SL::Template::Plugin::JSON;
+
+use JSON ();
+use Carp qw(croak);
+use base qw(Template::Plugin);
+
+our $VERSION = "0.06";
+
+sub new {
+  my ($class, $context, $args) = @_;
+
+  my $self = bless {context => $context, json_args => $args }, $class;
+
+  $context->define_vmethod($_, json => sub { $self->json(@_) }) for qw(hash list scalar);
+
+  return $self;
+}
+
+sub json_converter {
+  my ($self, %params) = @_;
+
+  if (!$self->{json}) {
+    $self->{json} = JSON->new->allow_nonref(1);
+
+    my $args = $self->{json_args};
+
+    for my $method (keys %$args) {
+      if ( $self->{json}->can($method) ) {
+        $self->{json}->$method( $args->{$method} );
+      }
+    }
+  }
+
+  return $self->{json};
+}
+
+sub json {
+  my ($self, $value) = @_;
+
+  $self->json_converter->encode($value);
+}
+
+sub json_decode {
+  my ( $self, $value ) = @_;
+
+  $self->json_converter->decode($value);
+}
+
+1;
+
+__END__
index 0ed2a87..476b60a 100644 (file)
@@ -291,6 +291,40 @@ sub date_tag {
   ) : '');
 }
 
+sub customer_picker {
+  my ($self, $name, $value, %params) = @_;
+  my $name_e    = _H($name);
+
+  $self->hidden_tag($name, (ref $value && $value->can('id')) ? $value->id : '') .
+  $self->input_tag("$name_e\_name", (ref $value && $value->can('name')) ? $value->name : '', %params) .
+  $self->javascript(<<JS);
+function autocomplete_customer (selector, column) {
+  \$(function(){ \$(selector).autocomplete({
+    source: function(req, rsp) {
+      \$.ajax({
+        url: 'controller.pl?action=Customer/ajax_autocomplete',
+        dataType: "json",
+        data: {
+          column: column,
+          term: req.term,
+          current: function() { \$('#$name_e').val() },
+          obsolete: 0,
+        },
+        success: function (data){ rsp(data) }
+      });
+    },
+    limit: 20,
+    delay: 50,
+    select: function(event, ui) {
+      \$('#$name_e').val(ui.item.id);
+      \$('#$name_e\_name').val(ui.item.name);
+    },
+  })});
+}
+autocomplete_customer('#$name_e\_name');
+JS
+}
+
 sub javascript_tag {
   my $self = shift;
   my $code = '';
index d7a7c35..8ad6220 100644 (file)
@@ -10,6 +10,8 @@ package SL::Template::Simple;
 
 use strict;
 
+use Scalar::Util qw(blessed);
+
 # Parameters:
 #   1. The template's file name
 #   2. A reference to the Form object
@@ -91,7 +93,19 @@ sub _get_loop_variable {
   my $form      = $self->{form};
   my $value;
 
-  if (($get_array || @indices) && (ref $form->{TEMPLATE_ARRAYS} eq 'HASH') && (ref $form->{TEMPLATE_ARRAYS}->{$var} eq 'ARRAY')) {
+  if ($var =~ m/\./) {
+    $value = $form;
+    for my $part (split(m/\./, $var)) {
+      if (ref($value) =~ m/^(?:Form|HASH)$/) {
+        $value = $value->{$part};
+      } elsif (blessed($value) && $value->can($part)) {
+        $value = $value->$part;
+      } else {
+        $value = '';
+        last;
+      }
+    }
+  } elsif (($get_array || @indices) && (ref $form->{TEMPLATE_ARRAYS} eq 'HASH') && (ref $form->{TEMPLATE_ARRAYS}->{$var} eq 'ARRAY')) {
     $value = $form->{TEMPLATE_ARRAYS}->{$var};
   } else {
     $value = $form->{$var};
index fc4f26f..c0a0e88 100644 (file)
@@ -54,9 +54,10 @@ our %translations = ('text'      => $locale->text('Free-form text'),
                      'timestamp' => $locale->text('Timestamp'),
                      'bool'      => $locale->text('Yes/No (Checkbox)'),
                      'select'    => $locale->text('Selection'),
+                     'customer'  => $locale->text('Customer'),
                      );
 
-our @types = qw(text textfield number date bool select); # timestamp
+our @types = qw(text textfield number date bool select customer); # timestamp
 
 our @modules = ({ module => 'CT',       description => $locale->text('Customers and vendors')          },
                 { module => 'IC',       description => $locale->text('Parts, services and assemblies') },
diff --git a/sql/Pg-upgrade2/oe_customer_vendor_fkeys.sql b/sql/Pg-upgrade2/oe_customer_vendor_fkeys.sql
new file mode 100644 (file)
index 0000000..0ac6c8b
--- /dev/null
@@ -0,0 +1,11 @@
+-- @tag: oe_customer_vendor_fkeys
+-- @encoding: utf-8
+-- @description: Foreign Keys für customer und vendor in oe
+-- @depends: release_2_6_3
+-- @timestamp: 1317380460
+UPDATE oe SET customer_id = NULL WHERE customer_id = 0;
+UPDATE oe SET   vendor_id = NULL WHERE   vendor_id = 0;
+
+
+ALTER TABLE oe ADD FOREIGN KEY (customer_id) REFERENCES customer(id);
+ALTER TABLE oe ADD FOREIGN KEY (vendor_id)   REFERENCES vendor(id);
index 335a77c..ba350da 100644 (file)
@@ -1,5 +1,6 @@
 [%- USE T8 %]
-[% USE HTML %]
+[%- USE HTML %]
+[%- USE L %]
 
 [%- SET var_name = HTML.escape(name_prefix) _ "cvar_" _ HTML.escape(var.name) _ HTML.escape(name_postfix) -%]
 
@@ -33,6 +34,9 @@
 [%- ELSIF var.type == 'timestamp' %]
 <input name="[% var_name %]" value="[% HTML.escape(var.value) %]">
 
+[%- ELSIF var.type == 'customer' %]
+[% L.customer_picker(var_name, var.value) %]
+
 [%- ELSIF var.type == 'select' %]
 
 <select name="[% var_name %]">
index 996525f..388b017 100644 (file)
@@ -1,5 +1,6 @@
 [%- USE T8 %]
 [%- USE HTML %]
+[%- USE L %]
 [%- BLOCK cvar_name %][% HTML.escape(cvar.name_prefix) _ "cvar_" _ HTML.escape(cvar.var.name) _ HTML.escape(cvar.name_postfix) -%][% END %]
 [%- BLOCK cvar_inputs %]
 [%- %]
@@ -32,6 +33,8 @@
  <option[% IF option.value == cvar.value %] selected[% END %]>[% HTML.escape(option.value) %]</option>
  [%- END %]
 </select>
+[%- ELSIF cvar.var.type == 'customer' %]
+[% render_input_blocks__cvar_name = PROCESS cvar_name %][% L.customer_picker(render_input_blocks__cvar_name, cvar.value) %]
 [%- ELSE %]
 <input name="[% PROCESS cvar_name %]" value="[% HTML.escape(cvar.value) %]" [%- IF cvar.var.maxlength %] maxlength="[% HTML.escape(cvar.var.maxlength) %]"[% END -%]>
 [%- END %]
index 0bf105e..db37d08 100644 (file)
@@ -46,6 +46,9 @@
      </select>
      <input name="cvar_[% HTML.escape(var.name) %]"[% IF var.maxlength %]maxlength="[% HTML.escape(var.maxlength) %]"[% END %]>
 
+     [%- ELSIF var.type == 'customer' %]
+     <input name="cvar_[% var.name | html %]">
+
      [% ELSIF var.type == 'select' %]
      <select name="cvar_[% HTML.escape(var.name) %]">
       <option value="" selected>---</option>
diff --git a/templates/webpages/ct/ajax_autocomplete2.html b/templates/webpages/ct/ajax_autocomplete2.html
new file mode 100644 (file)
index 0000000..f4a44e3
--- /dev/null
@@ -0,0 +1,11 @@
+[%- USE HTML %][% USE JSON %][
+[%- FOREACH customer = SELF.customers %]
+ {
+   "value": [% customer.${SELF.value}.json %],
+   "label": [% customer.displayable_name.json %],
+   "id": [% customer.id.json %],
+   "customernumber": [% customer.customernumber.json %],
+   "name": [% customer.name.json %]
+  }[% ',' UNLESS loop.last %]
+[%- END %]
+]
diff --git a/templates/webpages/ct/testpage.html b/templates/webpages/ct/testpage.html
new file mode 100644 (file)
index 0000000..e8ba838
--- /dev/null
@@ -0,0 +1,40 @@
+[% USE L %]
+[% USE T8 %]
+[% USE LxERP %]
+[% L.javascript_tag('jquery-ui') %]
+<link rel="stylesheet" href="css/ui-lightness/jquery-ui-1.8.12.custom.css" type="text/css" />
+
+<p>Pick a customer</p>
+id: [% L.input_tag('customer_id', '') %]
+nr: [% L.input_tag('customer_customernumber', '') %]
+desc: [% L.input_tag('customer_name', '') %]
+
+<script type='text/javascript'>
+function autocomplete_customer (selector, column) {
+  $(function(){ $(selector).autocomplete({
+    source: function(req, rsp) {
+      $.ajax({
+        url: 'controller.pl?action=Customer/ajax_autocomplete',
+        dataType: "json",
+        data: {
+          column: column,
+          term: req.term,
+          current: function() { $('#customer_id').val() },
+          obsolete: 0,
+        },
+        success: function (data){ rsp(data) }
+      });
+    },
+    limit: 20,
+    delay: 50,
+    select: function(event, ui) {
+      $('#customer_id').val(ui.item.id);
+      $('#customer_customernumber').val(ui.item.customernumber);
+      $('#customer_name').val(ui.item.name);
+    },
+  })});
+}
+//autocomplete_customer('#customer_customernumber', 'customernumber');
+autocomplete_customer('#customer_name', '');
+</script>
+
diff --git a/templates/webpages/part/ajax_autocomplete.html b/templates/webpages/part/ajax_autocomplete.html
new file mode 100644 (file)
index 0000000..53fa6f0
--- /dev/null
@@ -0,0 +1,13 @@
+[%- USE HTML %][% USE JSON %][
+[%- FOREACH part = SELF.parts %]
+[%- ajax_autocomplete__label = part.partnumber _ " " _ part.description %]
+ {
+   "value": [% part.${SELF.value}.json %],
+   "label": [% ajax_autocomplete__label.json %],
+   "id": [% part.id.json %],
+   "partnumber": [% part.partnumber.json %],
+   "description": [% part.description.json %],
+   "type": [% part.type.json %]
+  }[% ',' UNLESS loop.last %]
+[%- END %]
+]
diff --git a/templates/webpages/part/testpage.html b/templates/webpages/part/testpage.html
new file mode 100644 (file)
index 0000000..551220f
--- /dev/null
@@ -0,0 +1,40 @@
+[% USE L %]
+[% USE T8 %]
+[% USE LxERP %]
+[% L.javascript_tag('jquery', 'jquery-ui') %]
+<link rel="stylesheet" href="css/ui-lightness/jquery-ui-1.8.12.custom.css" type="text/css" />
+
+<p>Pick a part</p>
+id: [% L.input_tag('part_id', '') %]
+nr: [% L.input_tag('part_partnumber', '') %]
+desc: [% L.input_tag('part_description', '') %]
+
+<script type='text/javascript'>
+function autocomplete_part (selector, column, type) {
+  $(function(){ $(selector).autocomplete({
+    source: function(req, rsp) {
+      $.ajax({
+        url: 'controller.pl?action=Part/ajax_autocomplete',
+        dataType: "json",
+        data: {
+          column: column,
+          term: req.term,
+          current: function() { $('#part_id').val() },
+          type: type,
+          obsolete: 0,
+        },
+        success: function (data){ rsp(data) }
+      });
+    },
+    limit: 20,
+    delay: 50,
+    select: function(event, ui) {
+      $('#part_id').val(ui.item.id);
+      $('#part_partnumber').val(ui.item.partnumber);
+      $('#part_description').val(ui.item.description);
+    },
+  })});
+}
+autocomplete_part('#part_partnumber', 'partnumber', ['part', 'assembly']);
+autocomplete_part('#part_description', 'description', ['part', 'assembly']);
+</script>