Verknüpfte Belege: beliebige Verknüpfungen hinzufügen können
authorMoritz Bunkus <m.bunkus@linet-services.de>
Fri, 1 Mar 2013 12:32:11 +0000 (13:32 +0100)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Fri, 1 Mar 2013 12:33:59 +0000 (13:33 +0100)
15 files changed:
SL/Controller/RecordLinks.pm
SL/DB/Manager/DeliveryOrder.pm
SL/DB/Manager/Invoice.pm
SL/DB/Manager/Order.pm
SL/DB/Manager/PurchaseInvoice.pm
SL/Presenter/Invoice.pm
SL/Presenter/Record.pm
css/presenter/record/record_list.css [new file with mode: 0644]
image/dialog-close.png [new file with mode: 0644]
locale/de/all
templates/webpages/presenter/record/empty_record_list.html [deleted file]
templates/webpages/presenter/record/grouped_record_list.html
templates/webpages/presenter/record/record_list.html
templates/webpages/record_links/add_filter.html [new file with mode: 0644]
templates/webpages/record_links/add_list.html [new file with mode: 0644]

index a761443..de03775 100644 (file)
@@ -4,13 +4,36 @@ use strict;
 
 use parent qw(SL::Controller::Base);
 
+use List::Util qw(first);
+
+use SL::DB::Helper::Mappings;
 use SL::DB::Order;
 use SL::DB::DeliveryOrder;
 use SL::DB::Invoice;
 use SL::DB::PurchaseInvoice;
+use SL::DB::RecordLink;
+use SL::JSON;
 use SL::Locale::String;
 
-__PACKAGE__->run_before('check_object_params', only => [ qw(ajax_list ajax_delete) ]);
+use Rose::Object::MakeMethods::Generic
+(
+  scalar => [ qw(object object_model object_id link_type link_direction link_type_desc) ],
+);
+
+__PACKAGE__->run_before('check_object_params', only => [ qw(ajax_list ajax_delete ajax_add_select_type ajax_add_filter ajax_add_list ajax_add_do) ]);
+__PACKAGE__->run_before('check_link_params',   only => [ qw(                                                           ajax_add_list ajax_add_do) ]);
+
+my @link_types = (
+  { title => t8('Sales quotation'),         type => 'sales_quotation',         model => 'Order',           number => 'quonumber', },
+  { title => t8('Sales Order'),             type => 'sales_order',             model => 'Order',           number => 'ordnumber', },
+  { title => t8('Sales delivery order'),    type => 'sales_delivery_order',    model => 'DeliveryOrder',   number => 'donumber',  },
+  { title => t8('Sales Invoice'),           type => 'invoice',                 model => 'Invoice',         number => 'invnumber', },
+  { title => t8('Request for Quotation'),   type => 'request_quotation',       model => 'Order',           number => 'quonumber', },
+  { title => t8('Purchase Order'),          type => 'purchase_order',          model => 'Order',           number => 'ordnumber', },
+  { title => t8('Purchase delivery order'), type => 'purchase_delivery_order', model => 'DeliveryOrder',   number => 'donumber',  },
+  { title => t8('Purchase Invoice'),        type => 'purchase_invoice',        model => 'PurchaseInvoice', number => 'invnumber', },
+);
+
 
 #
 # actions
@@ -20,15 +43,13 @@ sub action_ajax_list {
   my ($self) = @_;
 
   eval {
-    my $model          = 'SL::DB::' . $::form->{object_model};
-    my $object         = $model->new(id => $::form->{object_id})->load || die $::locale->text("Record not found");
-    my $linked_records = $object->linked_records(direction => 'both');
+    my $linked_records = $self->object->linked_records(direction => 'both');
     my $output         = SL::Presenter->get->grouped_record_list(
       $linked_records,
       with_columns      => [ qw(record_link_direction) ],
       edit_record_links => 1,
-      object_model      => $::form->{object_model},
-      object_id         => $::form->{object_id},
+      object_model      => $self->object_model,
+      object_id         => $self->object_id,
     );
     $self->render(\$output, { layout => 0, process => 0 });
 
@@ -41,16 +62,13 @@ sub action_ajax_list {
 sub action_ajax_delete {
   my ($self) = @_;
 
-  my $prefix = $::form->{form_prefix} || 'record_links';
-  foreach my $str (@{ $::form->{"${prefix}_delete"} || [] }) {
+  foreach my $str (@{ $::form->{record_links_delete} || [] }) {
     my ($from_table, $from_id, $to_table, $to_id) = split m/__/, $str, 4;
     $from_id *= 1;
     $to_id   *= 1;
 
     next if !$from_table || !$from_id || !$to_table || !$to_id;
 
-    # $::lxdebug->message(0, "INSERT INTO record_links (from_table, from_id, to_table, to_id) VALUES ('${from_table}', ${from_id}, '${to_table}', ${to_id});");
-
     SL::DB::Manager::RecordLink->delete_all(where => [
       from_table => $from_table,
       from_id    => $from_id,
@@ -62,6 +80,87 @@ sub action_ajax_delete {
   $self->action_ajax_list;
 }
 
+sub action_ajax_add_filter {
+  my ($self) = @_;
+
+  my $presenter = $self->presenter;
+
+  my @link_type_select = map { [ $_->{type}, $_->{title} ] } @link_types;
+  my @projects         = map { [ $_->id, $presenter->project($_, display => 'inline', style => 'both', no_link => 1) ] } @{ SL::DB::Manager::Project->get_all_sorted };
+  my $is_sales         = $self->object->can('customer_id') && $self->object->customer_id;
+
+  $self->render(
+    'record_links/add_filter',
+    { layout          => 0 },
+    is_sales          => $is_sales,
+    DEFAULT_LINK_TYPE => $is_sales ? 'sales_quotation' : 'request_quotation',
+    LINK_TYPES        => \@link_type_select,
+    PROJECTS          => \@projects,
+  );
+}
+
+sub action_ajax_add_list {
+  my ($self) = @_;
+
+  my $manager = 'SL::DB::Manager::' . $self->link_type_desc->{model};
+  my $vc      = $self->link_type =~ m/sales_|^invoice$/ ? 'customer' : 'vendor';
+
+  my @where = $manager->type_filter($self->link_type);
+  push @where, ("${vc}.${vc}number"     => { ilike => '%' . $::form->{vc_number} . '%' })               if $::form->{vc_number};
+  push @where, ("${vc}.name"            => { ilike => '%' . $::form->{vc_name}   . '%' })               if $::form->{vc_name};
+  push @where, (transaction_description => { ilike => '%' . $::form->{transaction_description} . '%' }) if $::form->{transaction_description};
+  push @where, (globalproject_id        => $::form->{globalproject_id})                                 if $::form->{globalproject_id};
+
+  my $objects = $manager->get_all_sorted(where => \@where, with_objects => [ $vc, 'globalproject' ]);
+  my $output  = $self->render(
+    'record_links/add_list',
+    { output      => 0 },
+    OBJECTS       => $objects,
+    vc            => $vc,
+    number_column => $self->link_type_desc->{number},
+  );
+
+  my %result = ( count => scalar(@{ $objects }), html => $output );
+
+  $self->render(\to_json(\%result), { type => 'json', process => 0 });
+}
+
+sub action_ajax_add_do {
+  my ($self, %params) = @_;
+
+  my $object_side = $self->link_direction eq 'from' ? 'from' : 'to';
+  my $link_side   = $object_side          eq 'from' ? 'to'   : 'from';
+  my $link_table  = SL::DB::Helper::Mappings::get_table_for_package($self->link_type_desc->{model});
+
+  foreach my $link_id (@{ $::form->{link_id} || [] }) {
+    # Check for existing reverse connections in order to avoid loops.
+    my @props = (
+      "${link_side}_table"   => $self->object->meta->table,
+      "${link_side}_id"      => $self->object_id,
+      "${object_side}_table" => $link_table,
+      "${object_side}_id"    => $link_id,
+    );
+
+    my $existing = SL::DB::Manager::RecordLink->get_all(where => \@props, limit => 1)->[0];
+    next if $existing;
+
+    # Check for existing connections in order to avoid duplicates.
+    @props = (
+      "${object_side}_table" => $self->object->meta->table,
+      "${object_side}_id"    => $self->object_id,
+      "${link_side}_table"   => $link_table,
+      "${link_side}_id"      => $link_id,
+    );
+
+    $existing = SL::DB::Manager::RecordLink->get_all(where => \@props, limit => 1)->[0];
+
+    SL::DB::RecordLink->new(@props)->save if !$existing;
+  }
+
+  $self->action_ajax_list;
+}
+
+
 #
 # filters
 #
@@ -69,7 +168,27 @@ sub action_ajax_delete {
 sub check_object_params {
   my ($self) = @_;
 
-  return $::form->{object_id} && ($::form->{object_model} =~ m/^(?:Order|DeliveryOrder|Invoice|PurchaseInvoice)$/);
+  my %models = map { ($_->{model} => 1 ) } @link_types;
+
+  $self->object_id(   $::form->{object_id});
+  $self->object_model($::form->{object_model});
+
+  die "Invalid object_model or object_id" if !$self->object_id || !$models{$self->object_model};
+
+  my $model = 'SL::DB::' . $self->object_model;
+  $self->object($model->new(id => $self->object_id)->load || die "Record not found");
+
+  return 1;
+}
+
+sub check_link_params {
+  my ($self) = @_;
+
+  $self->link_type(     $::form->{link_type});
+  $self->link_type_desc((first { $_->{type} eq $::form->{link_type} } @link_types)                || die "Invalid link_type");
+  $self->link_direction($::form->{link_direction} =~ m/^(?:from|to)$/ ? $::form->{link_direction} :  die "Invalid link_direction");
+
+  return 1;
 }
 
 1;
index 1b76830..fa67f66 100644 (file)
@@ -2,8 +2,10 @@ package SL::DB::Manager::DeliveryOrder;
 
 use strict;
 
-use SL::DB::Helper::Manager;
-use base qw(SL::DB::Helper::Manager);
+use parent qw(SL::DB::Helper::Manager);
+
+use SL::DB::Helper::Paginated;
+use SL::DB::Helper::Sorted;
 
 sub object_class { 'SL::DB::DeliveryOrder' }
 
@@ -19,4 +21,31 @@ sub type_filter {
   die "Unknown type $type";
 }
 
+sub _sort_spec {
+  return (
+    default                   => [ 'transdate', 1 ],
+    nulls                     => {
+      transaction_description => 'FIRST',
+      customer_name           => 'FIRST',
+      vendor_name             => 'FIRST',
+      default                 => 'LAST',
+    },
+    columns                   => {
+      SIMPLE                  => 'ALL',
+      customer                => 'customer.name',
+      vendor                  => 'vendor.name',
+      globalprojectnumber     => 'lower(globalproject.projectnumber)',
+
+      # Bug in Rose::DB::Object: the next should be
+      # "globalproject.project_type.description". This workaround will
+      # only work if no other table with "project_type" is visible in
+      # the current query
+      globalproject_type      => 'lower(project_type.description)',
+
+      map { ( $_ => "lower(delivery_orders.$_)" ) } qw(donumber ordnumber cusordnumber oreqnumber shippingpoint shipvia notes intnotes transaction_description),
+    });
+}
+
+sub default_objects_per_page { 40 }
+
 1;
index c3a9a7b..493789e 100644 (file)
@@ -2,7 +2,10 @@ package SL::DB::Manager::Invoice;
 
 use strict;
 
-use base qw(SL::DB::Helper::Manager);
+use parent qw(SL::DB::Helper::Manager);
+
+use SL::DB::Helper::Paginated;
+use SL::DB::Helper::Sorted;
 
 sub object_class { 'SL::DB::Invoice' }
 
@@ -13,7 +16,7 @@ sub type_filter {
   my $type  = lc(shift || '');
 
   return (or  => [ invoice => 0, invoice => undef                                               ]) if $type eq 'ar_transaction';
-  return (and => [ invoice => 1, amount  => { ge => 0 }, or => [ storno => 0, storno => undef ] ]) if $type eq 'invoice';
+  return (and => [ invoice => 1, amount  => { ge => 0 }, or => [ storno => 0, storno => undef ] ]) if $type =~ m/^(?:sales_)?invoice$/;
   return (and => [ invoice => 1, amount  => { lt => 0 }, or => [ storno => 0, storno => undef ] ]) if $type eq 'credit_note';
   return (and => [ invoice => 1, amount  => { lt => 0 },         storno => 1                    ]) if $type =~ m/(?:invoice_)?storno/;
   return (and => [ invoice => 1, amount  => { ge => 0 },         storno => 1                    ]) if $type eq 'credit_note_storno';
@@ -21,4 +24,29 @@ sub type_filter {
   die "Unknown type $type";
 }
 
+sub _sort_spec {
+  return (
+    default                   => [ 'transdate', 1 ],
+    nulls                     => {
+      transaction_description => 'FIRST',
+      customer_name           => 'FIRST',
+      default                 => 'LAST',
+    },
+    columns                   => {
+      SIMPLE                  => 'ALL',
+      customer                => 'customer.name',
+      globalprojectnumber     => 'lower(globalproject.projectnumber)',
+
+      # Bug in Rose::DB::Object: the next should be
+      # "globalproject.project_type.description". This workaround will
+      # only work if no other table with "project_type" is visible in
+      # the current query
+      globalproject_type      => 'lower(project_type.description)',
+
+      map { ( $_ => "lower(ar.$_)" ) } qw(invnumber ordnumber quonumber cusordnumber shippingpoint shipvia notes intnotes transaction_description),
+    });
+}
+
+sub default_objects_per_page { 40 }
+
 1;
index 3c76073..69aa266 100644 (file)
@@ -30,11 +30,13 @@ sub _sort_spec {
     nulls                     => {
       transaction_description => 'FIRST',
       customer_name           => 'FIRST',
+      vendor_name             => 'FIRST',
       default                 => 'LAST',
     },
     columns                   => {
       SIMPLE                  => 'ALL',
       customer                => 'customer.name',
+      vendor                  => 'vendor.name',
       globalprojectnumber     => 'lower(globalproject.projectnumber)',
       map { ( $_ => "lower(oe.$_)" ) } qw(ordnumber quonumber cusordnumber shippingpoint shipvia notes intnotes transaction_description),
     });
index 1cc5737..2dd3fad 100644 (file)
@@ -2,8 +2,10 @@ package SL::DB::Manager::PurchaseInvoice;
 
 use strict;
 
-use SL::DB::Helper::Manager;
-use base qw(SL::DB::Helper::Manager);
+use parent qw(SL::DB::Helper::Manager);
+
+use SL::DB::Helper::Paginated;
+use SL::DB::Helper::Sorted;
 
 sub object_class { 'SL::DB::PurchaseInvoice' }
 
@@ -14,10 +16,35 @@ sub type_filter {
   my $type  = lc(shift || '');
 
   return (or  => [ invoice => 0, invoice => undef                       ]) if $type eq 'ap_transaction';
-  return (and => [ invoice => 1, or => [ storno => 0, storno => undef ] ]) if $type eq 'invoice';
+  return (and => [ invoice => 1, or => [ storno => 0, storno => undef ] ]) if $type =~ m/^(?:purchase_)?invoice$/;
   return (and => [ invoice => 1,         storno => 1                    ]) if $type =~ m/(?:invoice_)?storno/;
 
   die "Unknown type $type";
 }
 
+sub _sort_spec {
+  return (
+    default                   => [ 'transdate', 1 ],
+    nulls                     => {
+      transaction_description => 'FIRST',
+      vendor_name             => 'FIRST',
+      default                 => 'LAST',
+    },
+    columns                   => {
+      SIMPLE                  => 'ALL',
+      vendor                  => 'vendor.name',
+      globalprojectnumber     => 'lower(globalproject.projectnumber)',
+
+      # Bug in Rose::DB::Object: the next should be
+      # "globalproject.project_type.description". This workaround will
+      # only work if no other table with "project_type" is visible in
+      # the current query
+      globalproject_type      => 'lower(project_type.description)',
+
+      map { ( $_ => "lower(ap.$_)" ) } qw(invnumber ordnumber quonumber shipvia notes intnotes transaction_description),
+    });
+}
+
+sub default_objects_per_page { 40 }
+
 1;
index f1aaf7f..c078450 100644 (file)
@@ -24,7 +24,7 @@ sub ar_transaction {
 sub purchase_invoice {
   my ($self, $invoice, %params) = @_;
 
-  return _is_ir_record($self, $invoice, 'is', %params);
+  return _is_ir_record($self, $invoice, 'ir', %params);
 }
 
 sub ap_transaction {
index db0caa5..18c7190 100644 (file)
@@ -20,8 +20,7 @@ sub _arrayify {
 sub grouped_record_list {
   my ($self, $list, %params) = @_;
 
-  %params                = map { exists $params{$_} ? ($_ => $params{$_}) : () } qw(edit_record_links form_prefix with_columns object_id object_model);
-  $params{form_prefix} ||= 'record_links';
+  %params    = map { exists $params{$_} ? ($_ => $params{$_}) : () } qw(edit_record_links with_columns object_id object_model);
 
   my %groups = _group_records($list);
   my $output = '';
@@ -38,14 +37,14 @@ sub grouped_record_list {
   $output .= _purchase_invoice_list(       $self, $groups{purchase_invoices},        %params) if $groups{purchase_invoices};
   $output .= _ar_transaction_list(         $self, $groups{ar_transactions},          %params) if $groups{ar_transactions};
 
-  $output  = $self->render('presenter/record/grouped_record_list', %params, output => $output, nownow => DateTime->now) if $output;
+  $output  = $self->render('presenter/record/grouped_record_list', %params, output => $output);
 
-  return $output || $self->empty_record_list;
+  return $output;
 }
 
 sub empty_record_list {
-  my ($self) = @_;
-  return $self->render('presenter/record/empty_record_list');
+  my ($self, %params) = @_;
+  return $self->grouped_record_list([], %params);
 }
 
 sub record_list {
@@ -121,8 +120,6 @@ sub record_list {
            alignment => $data[0]->{columns}->[$_]->{alignment},
          }, (0..scalar(@columns) - 1);
 
-  $params{form_prefix} ||= 'record_links';
-
   return $self->render(
     'presenter/record/record_list',
     %params,
@@ -168,6 +165,7 @@ sub _sales_quotation_list {
   return $self->record_list(
     $list,
     title   => $::locale->text('Sales Quotations'),
+    type    => 'sales_quotation',
     columns => [
       [ $::locale->text('Quotation Date'),          'transdate'                                                                ],
       [ $::locale->text('Quotation Number'),        sub { $self->sales_quotation($_[0], display => 'table-cell') }   ],
@@ -187,9 +185,10 @@ sub _request_quotation_list {
   return $self->record_list(
     $list,
     title   => $::locale->text('Request Quotations'),
+    type    => 'request_quotation',
     columns => [
       [ $::locale->text('Quotation Date'),          'transdate'                                                                ],
-      [ $::locale->text('Quotation Number'),        sub { $self->sales_quotation($_[0], display => 'table-cell') }   ],
+      [ $::locale->text('Quotation Number'),        sub { $self->request_quotation($_[0], display => 'table-cell') }   ],
       [ $::locale->text('Vendor'),                  'vendor'                                                                   ],
       [ $::locale->text('Net amount'),              'netamount'                                                                ],
       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
@@ -206,6 +205,7 @@ sub _sales_order_list {
   return $self->record_list(
     $list,
     title   => $::locale->text('Sales Orders'),
+    type    => 'sales_order',
     columns => [
       [ $::locale->text('Order Date'),              'transdate'                                                                ],
       [ $::locale->text('Order Number'),            sub { $self->sales_order($_[0], display => 'table-cell') }   ],
@@ -226,9 +226,10 @@ sub _purchase_order_list {
   return $self->record_list(
     $list,
     title   => $::locale->text('Purchase Orders'),
+    type    => 'purchase_order',
     columns => [
       [ $::locale->text('Order Date'),              'transdate'                                                                ],
-      [ $::locale->text('Order Number'),            sub { $self->sales_order($_[0], display => 'table-cell') }   ],
+      [ $::locale->text('Order Number'),            sub { $self->purchase_order($_[0], display => 'table-cell') }   ],
       [ $::locale->text('Request for Quotation'),   'quonumber' ],
       [ $::locale->text('Vendor'),                  'vendor'                                                                 ],
       [ $::locale->text('Net amount'),              'netamount'                                                                ],
@@ -246,6 +247,7 @@ sub _sales_delivery_order_list {
   return $self->record_list(
     $list,
     title   => $::locale->text('Sales Delivery Orders'),
+    type    => 'sales_delivery_order',
     columns => [
       [ $::locale->text('Delivery Order Date'),     'transdate'                                                                ],
       [ $::locale->text('Delivery Order Number'),   sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
@@ -266,9 +268,10 @@ sub _purchase_delivery_order_list {
   return $self->record_list(
     $list,
     title   => $::locale->text('Purchase Delivery Orders'),
+    type    => 'purchase_delivery_order',
     columns => [
       [ $::locale->text('Delivery Order Date'),     'transdate'                                                                ],
-      [ $::locale->text('Delivery Order Number'),   sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Delivery Order Number'),   sub { $self->purchase_delivery_order($_[0], display => 'table-cell') } ],
       [ $::locale->text('Order Number'),            'ordnumber' ],
       [ $::locale->text('Vendor'),                  'vendor'                                                                 ],
       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
@@ -286,6 +289,7 @@ sub _sales_invoice_list {
   return $self->record_list(
     $list,
     title   => $::locale->text('Sales Invoices'),
+    type    => 'sales_invoice',
     columns => [
       [ $::locale->text('Invoice Date'),            'transdate'               ],
       [ $::locale->text('Invoice Number'),          sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
@@ -306,9 +310,10 @@ sub _purchase_invoice_list {
   return $self->record_list(
     $list,
     title   => $::locale->text('Purchase Invoices'),
+    type    => 'purchase_invoice',
     columns => [
       [ $::locale->text('Invoice Date'),                 'transdate'               ],
-      [ $::locale->text('Invoice Number'),               sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Invoice Number'),               sub { $self->purchase_invoice($_[0], display => 'table-cell') } ],
       [ $::locale->text('Request for Quotation Number'), 'quonumber' ],
       [ $::locale->text('Order Number'),                 'ordnumber' ],
       [ $::locale->text('Vendor'),                       'vendor'                 ],
@@ -326,6 +331,7 @@ sub _ar_transaction_list {
   return $self->record_list(
     $list,
     title   => $::locale->text('AR Transactions'),
+    type    => 'ar_transaction',
     columns => [
       [ $::locale->text('Invoice Date'),            'transdate'               ],
       [ $::locale->text('Invoice Number'),          sub { $self->ar_transaction($_[0], display => 'table-cell') } ],
@@ -344,9 +350,10 @@ sub _ap_transaction_list {
   return $self->record_list(
     $list,
     title   => $::locale->text('AP Transactions'),
+    type    => 'ap_transaction',
     columns => [
       [ $::locale->text('Invoice Date'),            'transdate'                      ],
-      [ $::locale->text('Invoice Number'),          sub { $self->ar_transaction($_[0 ], display => 'table-cell') } ],
+      [ $::locale->text('Invoice Number'),          sub { $self->ap_transaction($_[0 ], display => 'table-cell') } ],
       [ $::locale->text('Vendor'),                  'vendor'                         ],
       [ $::locale->text('Net amount'),              'netamount'                      ],
       [ $::locale->text('Paid'),                    'paid'                           ],
diff --git a/css/presenter/record/record_list.css b/css/presenter/record/record_list.css
new file mode 100644 (file)
index 0000000..59ab177
--- /dev/null
@@ -0,0 +1,37 @@
+/* the overlayed element */
+.record_list_overlay {
+  position: fixed;
+  top: 50%;
+  margin-top: -250px;
+  height: 500px;
+
+  left: 50%;
+  margin-left: -400px;
+  width: 800px;
+
+  background-color: #fff;
+  border: 1px solid #333;
+
+  /* CSS3 styling for latest browsers */
+  box-shadow: 0 0 90px 5px #000;
+  -moz-box-shadow: 0 0 90px 5px #000;
+  -webkit-box-shadow: 0 0 90px #000;
+
+  padding: 10px;
+}
+
+.record_list_overlay .overlay_content {
+  width: 790px;
+  height: 490px;
+  overflow: auto;
+}
+
+.record_list_overlay .close {
+  background-image: url(../../../image/dialog-close.png);
+  position: absolute;
+  right: -16px;
+  top: -16px;
+  cursor: pointer;
+  height: 32px;
+  width: 32px;
+}
diff --git a/image/dialog-close.png b/image/dialog-close.png
new file mode 100644 (file)
index 0000000..b049b68
Binary files /dev/null and b/image/dialog-close.png differ
index a7e2ef1..6b05e83 100644 (file)
@@ -152,6 +152,8 @@ $self->{texts} = {
   'Add and edit units'          => 'Einheiten erfassen und bearbeiten',
   'Add bank account'            => 'Bankkonto erfassen',
   'Add custom variable'         => 'Benutzerdefinierte Variable erfassen',
+  'Add link: select records to link with' => 'Verknüpfungen hinzufügen: zu verknüpfende Belege auswählen',
+  'Add links'                   => 'Verknüpfungen hinzufügen',
   'Add note'                    => 'Notiz erfassen',
   'Add unit'                    => 'Einheit hinzuf&uuml;gen',
   'Address'                     => 'Adresse',
@@ -209,6 +211,7 @@ $self->{texts} = {
   'Are you sure you want to delete this payment term?' => 'Wollen Sie diese Zahlungsbedingungen wirklich löschen?',
   'Are you sure you want to remove the marked entries from the queue?' => 'Sind Sie sicher, dass die markierten Einträge von der Warteschlange gelöscht werden sollen?',
   'Are you sure you want to update the prices' => 'Sind Sie sicher, dass Sie die Preise aktualisieren wollen?',
+  'Are you sure?'               => 'Sind Sie sicher?',
   'Article Code'                => 'Artikelkürzel',
   'Article Code missing!'       => 'Artikelkürzel fehlt',
   'Article type (see below)'    => 'Artikeltyp (siehe unten)',
@@ -541,7 +544,7 @@ $self->{texts} = {
   'Customer/Vendor'             => 'Kunde/Lieferant',
   'Customer/Vendor (database ID)' => 'Kunde/Lieferant (Datenbank-ID)',
   'Customer/Vendor Name'        => 'Kunde/Lieferant',
-  'Customer/Vendor Number'      => 'Kundennummer/Lieferantennummer',
+  'Customer/Vendor Number'      => 'Kunden-/Lieferantennummer',
   'Customername'                => 'Kundenname',
   'Customernumberinit'          => 'Kunden-/Lieferantennummernkreis',
   'Customers'                   => 'Kunden',
@@ -1127,6 +1130,7 @@ $self->{texts} = {
   'Line and column'             => 'Zeile und Spalte',
   'Line endings'                => 'Zeilenumbr&uuml;che',
   'Link direction'              => 'Verknüpfungsrichtung',
+  'Link to'                     => 'Verknüpfen mit',
   'Linked Records'              => 'Verknüpfte Belege',
   'List Accounts'               => 'Konten anzeigen',
   'List Languages'              => 'Sprachen anzeigen',
@@ -1569,7 +1573,6 @@ $self->{texts} = {
   'Reconciliation'              => 'Kontenabgleich',
   'Record Vendor Invoice'       => 'Einkaufsrechnung erfassen',
   'Record in'                   => 'Buchen auf',
-  'Record not found'            => 'Objekt nicht gefunden',
   'Recorded Tax'                => 'Gespeicherte Steuern',
   'Recorded taxkey'             => 'Gespeicherter Steuerschlüssel',
   'Reference'                   => 'Referenz',
@@ -1676,6 +1679,7 @@ $self->{texts} = {
   'Save settings as'            => 'Einstellungen speichern unter',
   'Saving the file \'%s\' failed. OS error message: %s' => 'Das Speichern der Datei \'%s\' schlug fehl. Fehlermeldung des Betriebssystems: %s',
   'Screen'                      => 'Bildschirm',
+  'Search'                      => 'Suchen',
   'Search AP Aging'             => 'Offene Verbindlichkeiten',
   'Search AR Aging'             => 'Offene Forderungen',
   'Search contacts'             => 'Ansprechpersonensuche',
@@ -1965,6 +1969,7 @@ $self->{texts} = {
   'The end date is the last day for which invoices will possibly be created.' => 'Das Enddatum ist das letztmögliche Datum, an dem eine Rechnung erzeugt wird.',
   'The execution schedule is invalid.' => 'Der Ausführungszeitplan ist ungültig.',
   'The execution type is invalid.' => 'Der Ausführungstyp ist ungültig.',
+  'The existing record has been created from the link target to add.' => 'Der bestehende Beleg wurde aus dem auszuwählenden Verknüpfungsziel erstellt.',
   'The factor is missing in row %d.' => 'Der Faktor fehlt in Zeile %d.',
   'The factor is missing.'      => 'Der Faktor fehlt.',
   'The first reason is that kivitendo contained a bug which resulted in the wrong taxkeys being recorded for transactions in which two entries are posted for the same chart with different taxkeys.' => 'Der erste Grund war ein Fehler in kivitendo, der dazu führte, dass bei einer Transaktion, bei der zwei Buchungen mit unterschiedlichen Steuerschlüsseln auf dasselbe Konto durchgeführt wurden, die falschen Steuerschlüssel gespeichert wurden.',
@@ -1987,6 +1992,7 @@ $self->{texts} = {
   'The group memberships have been saved.' => 'Die Gruppenmitgliedschaften wurden gespeichert.',
   'The group name is missing.'  => 'Der Gruppenname fehlt.',
   'The items are imported accoring do their number "X" regardless of the column order inside the file.' => 'Die Einträge werden in der Reihenfolge ihrer Indizes "X" unabhängig von der Spaltenreihenfolge in der Datei importiert.',
+  'The link target to add has been created from the existing record.' => 'Das auszuwählende Verknüpfungsziel wurde aus dem bestehenden Beleg erstellt.',
   'The list has been printed.'  => 'Die Liste wurde ausgedruckt.',
   'The long description is missing.' => 'Der Langtext fehlt.',
   'The name in row %d has already been used before.' => 'Der Name in Zeile %d wurde vorher bereits benutzt.',
diff --git a/templates/webpages/presenter/record/empty_record_list.html b/templates/webpages/presenter/record/empty_record_list.html
deleted file mode 100644 (file)
index d082315..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-[% USE LxERP %]
-<p class="message_hint">[% LxERP.t8('No data was found.') %]</p>
index 0016612..82c16f3 100644 (file)
@@ -1,19 +1,45 @@
 [%- USE LxERP -%][%- USE L -%][%- USE HTML -%][%- USE JavaScript -%]
 
-<div id="[% form_prefix %]_list">
- <p>[% nownow %]</p>
+<div id="record_links_list">
+ [%- IF output %]
+  [% output %]
+ [%- ELSE %]
+  <p class="message_hint">[% LxERP.t8('No data was found.') %]</p>
+ [%- END %]
 
- [% output %]
-
-[%- IF edit_record_links %]
- <div>
-  [% L.button_tag(form_prefix _ '_delete()', LxERP.t8('Delete links')) %]
- </div>
+ [%- IF edit_record_links %]
+  <div>
+   [% L.button_tag('record_links_add()', LxERP.t8('Add links')) %]
+   [% IF output %]
+    [% L.button_tag('record_links_delete()', LxERP.t8('Delete links')) %]
+   [%- END %]
+  </div>
 
  <script type="text/javascript">
   <!--
-function [% form_prefix %]_delete() {
-  var checkboxes = $('.record_links_delete').filter(function () { return $(this).attr('checked'); });
+$(function() {
+
+});
+
+function record_links_add() {
+  var url = "controller.pl?action=RecordLinks/ajax_add_filter&object_model=[% JavaScript.escape(object_model) %]&object_id=[% JavaScript.escape(object_id) %]&";
+  var id  = 'record_links_add';
+
+  $('#' + id).remove();
+  var div     = $('<div id="' + id + '" class="jqmWindow record_list_overlay"></div>').hide().appendTo('body');
+  var close   = $('<div class="close"></div>').appendTo(div);
+  var content = $('<div class="overlay_content"></div>').appendTo(div);
+  div.jqm({ modal: true });
+  div.jqmShow();
+  $.ajax({ url: url, success: function(new_html) { $(content).html(new_html); } });
+  $(close).click(function() {
+    div.jqmHide();
+    div.remove();
+  });
+}
+
+function record_links_delete() {
+  var checkboxes = $('.record_links_delete').filter(function () { return $(this).prop('checked'); });
 
   if ((checkboxes.size() == 0) || !confirm('[% LxERP.t8('Do you really want to delete the selected links?') %]'))
     return false;
@@ -27,7 +53,7 @@ function [% form_prefix %]_delete() {
   $.ajax({
     url:     "controller.pl?" + checkboxes.serialize(),
     data:    data,
-    success: function(new_data) { $('#[% form_prefix %]_list').replaceWith(new_data); }
+    success: function(new_data) { $('#record_links_list').replaceWith(new_data); }
   });
 
   return false;
index e1395a2..e75657a 100644 (file)
@@ -2,10 +2,12 @@
 <div class="listtop">[%- P.escape(title) %]</div>
 
 <div style="padding-bottom: 15px">
- <table style="width: 100%">
+ <table style="width: 100%" id="record_list_[% type %]">
   <thead>
    <tr>
-    [%- IF edit_record_links %]<th class="listheading"></th>[%- END %]
+    [%- IF edit_record_links %]
+    <th class="listheading">[% L.checkbox_tag('record_links_delete_checkall_' _ type) %]</th>
+    [%- END %]
     [%- FOREACH column = TABLE_HEADER %]
     <th class="listheading"[% IF column.alignment %] align="[% column.alignment %]"[% END %]>[%- P.escape(column.value) %]</th>
     [%- END %]
@@ -16,7 +18,7 @@
    [%- FOREACH row = TABLE_ROWS %]
    <tr class="listrow[% loop.count % 2 %]">
     [%- IF edit_record_links %]
-     <td>[%- L.checkbox_tag(form_prefix _ '_delete[]', 'value'=row.record_link.from_table _ '__' _ row.record_link.from_id _ '__' _ row.record_link.to_table _ '__' _ row.record_link.to_id, 'class'='record_links_delete') %]</td>
+     <td>[%- L.checkbox_tag('record_links_delete[]', 'value'=row.record_link.from_table _ '__' _ row.record_link.from_id _ '__' _ row.record_link.to_table _ '__' _ row.record_link.to_id, 'class'='record_links_delete') %]</td>
     [%- END %]
     [%- FOREACH column = row.columns %]
     <td[% IF column.alignment %] align="[% column.alignment %]"[% END %]>
@@ -30,3 +32,9 @@
   </tbody>
  </table>
 </div>
+
+[% IF edit_record_links %]
+<script type="text/javascript">
+$('#record_links_delete_checkall_[% type %]').checkall("#record_list_[% type %] tbody :checkbox");
+</script>
+[%- END %]
diff --git a/templates/webpages/record_links/add_filter.html b/templates/webpages/record_links/add_filter.html
new file mode 100644 (file)
index 0000000..1ef213b
--- /dev/null
@@ -0,0 +1,96 @@
+[%- USE L -%][%- USE LxERP -%][%- USE JavaScript -%]
+[%- SET style='width: 500px' %]
+<div class="listtop">[%- LxERP.t8("Add link: select records to link with") %]</div>
+
+
+<form method="post" action="controller.pl">
+ [% L.hidden_tag('object_model',   SELF.object_model) %]
+ [% L.hidden_tag('object_id',      SELF.object_id) %]
+
+ <table>
+  <tr>
+   <td>[%- LxERP.t8("Link to") %]:</td>
+   <td>[% L.select_tag('link_type', LINK_TYPES, default=DEFAULT_LINK_TYPE, style=style) %]</td>
+  </tr>
+
+  <tr>
+   <td>[%- LxERP.t8("Link direction") %]:</td>
+   <td>[% L.select_tag('link_direction',
+                       [ [ 'from', LxERP.t8("The link target to add has been created from the existing record."), ],
+                         [ 'to',   LxERP.t8("The existing record has been created from the link target to add."), ], ],
+                       style=style) %]</td>
+  </tr>
+
+  <tr>
+   <td>[%- LxERP.t8("Customer/Vendor Number") %]:</td>
+   <td>[% L.input_tag('vc_number', is_sales ? SELF.object.customer.customernumber : SELF.object.vendor.vendornumber, style=style) %]</td>
+  </tr>
+
+  <tr>
+   <td>[%- LxERP.t8("Customer/Vendor Name") %]:</td>
+   <td>[% L.input_tag('vc_name', is_sales ? SELF.object.customer.name : SELF.object.vendor.name, style=style) %]</td>
+  </tr>
+
+  <tr>
+   <td>[%- LxERP.t8("Project") %]:</td>
+   <td>[% L.select_tag('project_id', PROJECTS, default=SELF.object.globalproject_id, with_empty=1, style=style) %]</td>
+  </tr>
+
+  <tr>
+   <td>[%- LxERP.t8("Transaction description") %]:</td>
+   <td>[% L.input_tag('transaction_description', '', style=style) %]</td>
+  </tr>
+ </table>
+
+ <p>
+  [% L.button_tag('filter_record_links()', LxERP.t8("Search")) %]
+  [% L.button_tag('add_selected_record_links()', LxERP.t8("Add links"), id='add_selected_record_links_button', disabled=1) %]
+  <a href="#" onclick="record_links_reset_form();">[%- LxERP.t8("Reset") %]</a>
+  <a href="#" onclick="record_links_cancel();">[% LxERP.t8("Cancel") %]</a>
+ </p>
+
+ <hr>
+
+ <div id="record_list_filtered_list"></div>
+
+</form>
+
+<script type="text/javascript">
+<!--
+$(function() {
+  $('.jqmWindow input[name=vc_name]').focus();
+});
+
+function record_links_reset_form() {
+  $('.jqmWindow form input[type=text]').val('');
+  $('.jqmWindow form select').prop('selectedIndex', 0);
+}
+
+function record_links_cancel() {
+  $('.jqmWindow').jqmHide();
+  $('.jqmWindow').remove();
+}
+
+function filter_record_links() {
+  var url="controller.pl?action=RecordLinks/ajax_add_list&" + $(".jqmWindow form").serialize();
+  $.ajax({
+    url: url,
+    success: function(new_data) {
+      $("#record_list_filtered_list").html(new_data['html']);
+      $('#add_selected_record_links_button').prop('disabled', new_data['count'] == 0);
+    }
+  });
+}
+
+function add_selected_record_links() {
+  var url="controller.pl?action=RecordLinks/ajax_add_do&" + $(".jqmWindow form").serialize();
+  $.ajax({
+    url: url,
+    success: function(new_html) {
+      $('#record_links_list').replaceWith(new_html);
+      record_links_cancel();
+    }
+  });
+}
+-->
+</script>
diff --git a/templates/webpages/record_links/add_list.html b/templates/webpages/record_links/add_list.html
new file mode 100644 (file)
index 0000000..9ca7b45
--- /dev/null
@@ -0,0 +1,34 @@
+[%- USE T8 -%][%- USE HTML -%][%- USE LxERP -%][%- USE P -%][%- USE L -%]
+[%- IF !OBJECTS.size %]
+<p class="message_hint">[% 'No data was found.' | $T8 %]</p>
+[%- ELSE %]
+<table width="100%">
+ <tr class="listheading">
+  <th>[% L.checkbox_tag('record_links_check_all') %]</th>
+  <th>[% IF vc == 'customer' %][%- LxERP.t8("Customer") %][%- ELSE %][%- LxERP.t8("Vendor") %][%- END %]</th>
+  <th>[%- LxERP.t8("Number") %]</th>
+  <th>[%- LxERP.t8("Date") %]</th>
+  <th>[%- LxERP.t8("Transaction description") %]</th>
+  <th>[%- LxERP.t8("Project") %]</th>
+ </tr>
+
+ [%- FOREACH object = OBJECTS %]
+ <tr class="listrow[% loop.count % 2 %]">
+  <td>[% L.checkbox_tag('link_id[]', value=object.id) %]</td>
+  <td>[%- HTML.escape(object.$vc.name) %]</td>
+  <td>[%- HTML.escape(object.$number_column) %]</td>
+  <td>[%- HTML.escape(object.transdate.to_kivitendo) %]</td>
+  <td>[%- HTML.escape(object.transaction_description) %]</td>
+  <td>[%- P.project(object.globalproject, no_link=1) %]</td>
+ </tr>
+ [%- END %]
+</table>
+
+<script type="text/javascript">
+ <!--
+$(function() {
+  $('#record_links_check_all').checkall('INPUT[name="link_id[]"]');
+});
+-->
+</script>
+[%- END %]