Merge branch 'bankerweiterung_und_skonto'
authorG. Richardson <information@kivitendo-premium.de>
Tue, 5 May 2015 07:56:42 +0000 (09:56 +0200)
committerG. Richardson <information@kivitendo-premium.de>
Tue, 5 May 2015 07:56:42 +0000 (09:56 +0200)
Conflicts:
doc/changelog
locale/de/all

71 files changed:
SL/AR.pm
SL/Auth.pm
SL/CT.pm
SL/ClientJS.pm
SL/Clipboard.pm
SL/Clipboard/Base.pm
SL/Common.pm
SL/Controller/Inventory.pm
SL/Controller/Project.pm
SL/DB/AuthUser.pm
SL/DB/BackgroundJob.pm
SL/DB/DeliveryOrder.pm
SL/DB/Helper/ALL.pm
SL/DB/Helper/AttrSorted.pm [new file with mode: 0644]
SL/DB/Helper/Mappings.pm
SL/DB/Invoice.pm
SL/DB/Letter.pm [new file with mode: 0644]
SL/DB/LetterDraft.pm [new file with mode: 0644]
SL/DB/MetaSetup/Default.pm
SL/DB/MetaSetup/Letter.pm [new file with mode: 0644]
SL/DB/MetaSetup/LetterDraft.pm [new file with mode: 0644]
SL/DB/Order.pm
SL/DB/PriceRule.pm
SL/DB/PriceRuleItem.pm
SL/DB/PurchaseInvoice.pm
SL/DN.pm
SL/DO.pm
SL/Dispatcher/AuthHandler/Admin.pm
SL/IC.pm
SL/IR.pm
SL/OE.pm
SL/PriceSource.pm
SL/PriceSource/Business.pm
SL/PriceSource/Discount.pm
SL/PriceSource/Pricegroup.pm
SL/PriceSource/Vendor.pm
bin/mozilla/ar.pl
bin/mozilla/ct.pl
bin/mozilla/do.pl
bin/mozilla/ic.pl
bin/mozilla/io.pl
bin/mozilla/ir.pl
bin/mozilla/is.pl
bin/mozilla/letter.pl
bin/mozilla/oe.pl
css/common.css
doc/changelog
index.html
js/client_js.js
locale/de/all
locale/de/special_chars
locale/en/all
scripts/installation_check.pl
sql/Pg-upgrade2/ar_ap_fix_notes_as_html_for_non_invoices.pl [new file with mode: 0644]
sql/Pg-upgrade2/convert_taxzone.pl
sql/Pg-upgrade2/delete_invalidated_custom_variables_for_parts.sql [new file with mode: 0644]
sql/Pg-upgrade2/remove_obsolete_trigger.sql
sql/Pg-upgrade2/taxzone_id_in_oe_delivery_orders.sql
sql/Pg-upgrade2/warehouse_add_delivery_order_items_stock_id.sql
t/006spellcheck.t
templates/webpages/am/config.html
templates/webpages/ar/search.html
templates/webpages/ct/search.html
templates/webpages/do/form_header.html
templates/webpages/do/search.html
templates/webpages/ic/search.html
templates/webpages/io/select_item.html
templates/webpages/ir/form_header.html
templates/webpages/letter/edit.html
templates/webpages/oe/form_header.html
templates/webpages/oe/search.html

index 7236196..81be410 100644 (file)
--- a/SL/AR.pm
+++ b/SL/AR.pm
@@ -571,6 +571,15 @@ sub ar_transactions {
     }
   };
 
+  my ($cvar_where, @cvar_values) = CVar->build_filter_query('module'         => 'CT',
+                                                            'trans_id_field' => 'c.id',
+                                                            'filter'         => $form,
+                                                           );
+  if ($cvar_where) {
+    $where .= qq| AND ($cvar_where)|;
+    push @values, @cvar_values;
+  }
+
   my @a = qw(transdate invnumber name);
   push @a, "employee" if $form->{l_employee};
   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
index e202b98..5629fd7 100644 (file)
@@ -930,20 +930,6 @@ sub all_rights_full {
   my $locale = $main::locale;
 
   my @all_rights = (
-    ["--crm",                          $locale->text("CRM optional software")],
-    ["crm_search",                     $locale->text("CRM search")],
-    ["crm_new",                        $locale->text("CRM create customers, vendors and contacts")],
-    ["crm_service",                    $locale->text("CRM services")],
-    ["crm_admin",                      $locale->text("CRM admin")],
-    ["crm_adminuser",                  $locale->text("CRM user")],
-    ["crm_adminstatus",                $locale->text("CRM status")],
-    ["crm_email",                      $locale->text("CRM send email")],
-    ["crm_termin",                     $locale->text("CRM termin")],
-    ["crm_opportunity",                $locale->text("CRM opportunity")],
-    ["crm_knowhow",                    $locale->text("CRM know how")],
-    ["crm_follow",                     $locale->text("CRM follow up")],
-    ["crm_notices",                    $locale->text("CRM notices")],
-    ["crm_other",                      $locale->text("CRM other")],
     ["--master_data",                  $locale->text("Master Data")],
     ["customer_vendor_edit",           $locale->text("Create customers and vendors. Edit all vendors. Edit only customers where salesman equals employee (login)")],
     ["customer_vendor_all_edit",       $locale->text("Create customers and vendors. Edit all vendors. Edit all customers")],
index 44adb40..cdf274b 100644 (file)
--- a/SL/CT.pm
+++ b/SL/CT.pm
@@ -76,8 +76,10 @@ sub search {
       "city"               => "ct.city",
       "country"            => "ct.country",
       "discount"           => "ct.discount",
+      "insertdate"         => "ct.itime",
       "salesman"           => "e.name",
-      "payment"            => "pt.description"
+      "payment"            => "pt.description",
+      "pricegroup"         => "pg.pricegroup",
     );
 
   $form->{sort} ||= "name";
@@ -92,7 +94,7 @@ sub search {
   }
   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
 
-  if ($sortorder !~ /(business|id|discount)/ && !$join_records) {
+  if ($sortorder !~ /(business|id|discount|itime)/ && !$join_records) {
     $sortorder  = "lower($sortorder) ${sortdir}";
   } else {
     $sortorder .= " ${sortdir}";
@@ -176,6 +178,16 @@ sub search {
     push(@values, conv_i($form->{salesman_id}));
   }
 
+  if($form->{insertdatefrom}) {
+    $where .= qq| AND (ct.itime::DATE >= ?)|;
+    push@values, conv_date($form->{insertdatefrom});
+  }
+
+  if($form->{insertdateto}) {
+    $where .= qq| AND (ct.itime::DATE <= ?)|;
+    push @values, conv_date($form->{insertdateto});
+  }
+
   # Nur Kunden finden, bei denen ich selber der Verkäufer bin
   # Gilt nicht für Lieferanten
   if ($cv eq 'customer' &&   !$main::auth->assert('customer_vendor_all_edit', 1)) {
@@ -202,14 +214,18 @@ sub search {
     push @values, $form->{addr_zipcode} . '%';
   }
 
+  my $pg_select = $form->{l_pricegroup} ? qq|, pg.pricegroup as pricegroup | : '';
+  my $pg_join   = $form->{l_pricegroup} ? qq|LEFT JOIN pricegroup pg ON (ct.klass = pg.id) | : '';
   my $query =
-    qq|SELECT ct.*, b.description AS business, e.name as salesman, |.
+    qq|SELECT ct.*, ct.itime::DATE AS insertdate, b.description AS business, e.name as salesman, | .
     qq|  pt.description as payment | .
+    $pg_select .
     (qq|, NULL AS invnumber, NULL AS ordnumber, NULL AS quonumber, NULL AS invid, NULL AS module, NULL AS formtype, NULL AS closed | x!! $join_records) .
     qq|FROM $cv ct | .
     qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
     qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
     qq|LEFT JOIN payment_terms pt ON (ct.payment_id = pt.id) | .
+    $pg_join .
     qq|WHERE $where|;
 
   my @saved_values = @values;
@@ -223,9 +239,10 @@ sub search {
       push(@values, @saved_values);
       $query .=
         qq| UNION | .
-        qq|SELECT ct.*, b.description AS business, e.name as salesman, |.
-        qq|  pt.description as payment, | .
-        qq|  a.invnumber, a.ordnumber, a.quonumber, a.id AS invid, | .
+        qq|SELECT ct.*, ct.itime::DATE AS insertdate, b.description AS business, e.name as salesman, | .
+        qq|  pt.description as payment | .
+        $pg_select .
+        qq|, a.invnumber, a.ordnumber, a.quonumber, a.id AS invid, | .
         qq|  '$module' AS module, 'invoice' AS formtype, | .
         qq|  (a.amount = a.paid) AS closed | .
         qq|FROM $cv ct | .
@@ -233,6 +250,7 @@ sub search {
         qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
         qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
         qq|LEFT JOIN payment_terms pt ON (ct.payment_id = pt.id) | .
+        $pg_join .
         qq|WHERE $where AND (a.invoice = '1')|;
     }
 
@@ -240,15 +258,17 @@ sub search {
       push(@values, @saved_values);
       $query .=
         qq| UNION | .
-        qq|SELECT ct.*, b.description AS business, e.name as salesman, |.
-        qq|  pt.description as payment, | .
-        qq|  ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
+        qq|SELECT ct.*, ct.itime::DATE AS insertdate, b.description AS business, e.name as salesman, | .
+        qq|  pt.description as payment | .
+        $pg_select .
+        qq|, ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
         qq|  'oe' AS module, 'order' AS formtype, o.closed | .
         qq|FROM $cv ct | .
         qq|JOIN oe o ON (o.${cv}_id = ct.id) | .
         qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
         qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
         qq|LEFT JOIN payment_terms pt ON (ct.payment_id = pt.id) | .
+        $pg_join .
         qq|WHERE $where AND (o.quotation = '0')|;
     }
 
@@ -256,15 +276,17 @@ sub search {
       push(@values, @saved_values);
       $query .=
         qq| UNION | .
-        qq|SELECT ct.*, b.description AS business, e.name as salesman, | .
-        qq|  pt.description as payment, | .
-        qq|  ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
+        qq|SELECT ct.*, ct.itime::DATE AS insertdate, b.description AS business, e.name as salesman, | .
+        qq|  pt.description as payment | .
+        $pg_select .
+        qq|, ' ' AS invnumber, o.ordnumber, o.quonumber, o.id AS invid, | .
         qq|  'oe' AS module, 'quotation' AS formtype, o.closed | .
         qq|FROM $cv ct | .
         qq|JOIN oe o ON (o.${cv}_id = ct.id) | .
         qq|LEFT JOIN business b ON (ct.business_id = b.id) | .
         qq|LEFT JOIN employee e ON (ct.salesman_id = e.id) | .
         qq|LEFT JOIN payment_terms pt ON (ct.payment_id = pt.id) | .
+        $pg_join .
         qq|WHERE $where AND (o.quotation = '1')|;
     }
   }
index 2eedbee..d4775b8 100644 (file)
@@ -118,6 +118,8 @@ my %supported_methods = (
   reinit_widgets         => 0,  # kivi.reinit_widgets()
   run                    => -1, # kivi.run(<TARGET>, <ARGS>)
   run_once_for           => 3,  # kivi.run_once_for(<TARGET>, <ARGS>)
+
+  scroll_into_view       => 1,  # $(<TARGET>)[0].scrollIntoView()
 );
 
 sub AUTOLOAD {
index 6f30071..ee3d363 100644 (file)
@@ -120,7 +120,7 @@ Rose::DB::Object instances
 =head1 OVERVIEW
 
 The clipboard can store an unlimited number of copies of
-Rose::DB::Object instance. The instances are dumped into trees using
+Rose::DB::Object instances. The instances are dumped into trees using
 L<Rose::DB::Object::Helpers/as_tree>. How much of such an object is
 copied depends on its type. For example, a dump of a customer object
 might also include the dumps of the shipping address and contact
@@ -147,7 +147,7 @@ C<SL::DB::RequirementSpecItem> would be
 C<SL::Clipboard::RequirementSpecItem>. These support classes must
 inherit from L<SL::Clipboard::Base> which offers almost a full set of
 support functions so that the actual specialized class has to do very
-litte.
+little.
 
 As the clipboard is session-based its contents will be lost when the
 session expires (either due to timeouts or to the user logging off).
@@ -163,7 +163,7 @@ Clears the clipboard (removes all entries).
 =item C<copy $object>
 
 Creates a dumped copy of C<$object> and stores that copy in the
-session. An unlimited number of copies of differeing types can be
+session. An unlimited number of copies of differing types can be
 made.
 
 Returns the instance of the copied object, a sub-class of
@@ -181,7 +181,7 @@ array is the one most recently copied.
 =item C<get_entry [$type]>
 
 Returns the most recently clipped object whose type matches the
-regular expression C<$type>. If C<$type> is not given then then then
+regular expression C<$type>. If C<$type> is not given then the
 most recently copied object is returned.
 
 If no such object exists C<undef> is returned instead.
index 47ccc24..fc0a16a 100644 (file)
@@ -180,13 +180,13 @@ L</dump>.
 
 =item C<to_object>
 
-Converts the dumped representation back to an Rose::DB::Object
+Converts the dumped representation back to a Rose::DB::Object
 instance. Several columns of the newly created object are cleared by
 C<to_object> itself: the primary key columns (if any) and the columns
 C<itime> and C<mtime> (if the object has such columns).
 
 This function should not be overwritten by sub-classes. Instead,
-functions can overwrite C<_fix_object> which can be used for sanitzing
+functions can overwrite C<_fix_object> which can be used for sanitizing
 the newly created object before handing it back to the caller.
 
 =item C<type>
index 33d4d41..bcaeb62 100644 (file)
@@ -583,6 +583,8 @@ sub get_webdav_folder {
     ($path, $number) = ("einkaufslieferscheine", $form->{donumber});
   } elsif ($form->{type} eq "credit_note") {
     ($path, $number) = ("gutschriften", $form->{invnumber});
+  } elsif ($form->{type} eq "letter") {
+    ($path, $number) = ("briefe", $form->{letternumber} );
   } elsif ($form->{vc} eq "customer") {
     ($path, $number) = ("rechnungen", $form->{invnumber});
   } elsif ($form->{vc} eq "vendor") {
@@ -610,7 +612,7 @@ sub copy_file_to_webdav_folder {
   # checks
   foreach my $item (qw(tmpdir tmpfile type)){
     next if $form->{$item};
-    $::lxdebug->message(LXDebug::WARN(), 'Missing parameter');
+    $::lxdebug->message(LXDebug::WARN(), 'Missing parameter:' . $item);
     $::form->error($::locale->text("Missing parameter for WebDAV file copy"));
   }
 
@@ -618,6 +620,7 @@ sub copy_file_to_webdav_folder {
 
   if (! $webdav_folder){
     $::lxdebug->leave_sub();
+    $::lxdebug->message(LXDebug::WARN(), 'Cannot check correct WebDAV folder');
     $::form->error($::locale->text("Cannot check correct WebDAV folder"));
     return undef;
   }
@@ -626,7 +629,7 @@ sub copy_file_to_webdav_folder {
 
   # maybe the path does not exist (automatic printing), see #2446
   if (!-d $complete_path) {
-    # we need a chdir and  restore old dir
+    # we need a chdir and restore old dir
     my $current_dir = POSIX::getcwd();
     chdir("$form->{cwd}");
     mkdir_with_parents($webdav_folder);
index 8e5a445..5bc780b 100644 (file)
@@ -59,6 +59,7 @@ sub action_stock {
       unit          => $self->unit,
       transfer_type => 'stock',
       chargenumber  => $::form->{chargenumber},
+      bestbefore    => $::form->{bestbefore},
       ean           => $::form->{ean},
       comment       => $::form->{comment},
     });
index 2c2498b..6049785 100644 (file)
@@ -57,11 +57,9 @@ sub action_list {
 
   $self->make_filter_summary;
 
-  my $projects = $self->models->get;
-
   $self->prepare_report;
 
-  $self->report_generator_list_objects(report => $self->{report}, objects => $projects);
+  $self->report_generator_list_objects(report => $self->{report}, objects => $self->models->get);
 }
 
 sub action_new {
@@ -218,15 +216,10 @@ sub prepare_report {
 
   map { $column_defs{$_}->{text} ||= $::locale->text( $self->models->get_sort_spec->{$_}->{title} ) } keys %column_defs;
 
-  if ( $report->{options}{output_format} =~ /^(pdf|csv)$/i ) {
-    $self->models->disable_plugin('paginated');
-  }
   $report->set_options(
     std_column_visibility => 1,
     controller_class      => 'Project',
     output_format         => 'HTML',
-    raw_top_info_text     => $self->render('project/report_top', { output => 0 }),
-    raw_bottom_info_text  => $self->render('project/report_bottom', { output => 0 }),
     title                 => $::locale->text('Projects'),
     allow_pdf_export      => 1,
     allow_csv_export      => 1,
@@ -235,8 +228,10 @@ sub prepare_report {
   $report->set_column_order(@columns);
   $report->set_export_options(qw(list filter));
   $report->set_options_from_form;
+  $self->models->disable_plugin('paginated') if $report->{options}{output_format} =~ /^(pdf|csv)$/i;
   $self->models->set_report_generator_sort_options(report => $report, sortable_columns => \@sortable);
   $report->set_options(
+    raw_top_info_text     => $self->render('project/report_top',    { output => 0 }),
     raw_bottom_info_text  => $self->render('project/report_bottom', { output => 0 }),
   );
 }
index 1eac6f9..a8d8ab5 100644 (file)
@@ -11,7 +11,7 @@ use SL::DB::Helper::Util;
 use constant CONFIG_VARS => qw(copies countrycode dateformat timeformat default_media default_printer_id
                                email favorites fax hide_cvar_search_options mandatory_departments menustyle name
                                numberformat show_form_details signature stylesheet taxincluded_checked tel
-                               template_format vclimit focus_position form_cvars_nr_cols);
+                               template_format vclimit focus_position form_cvars_nr_cols item_multiselect);
 
 __PACKAGE__->meta->add_relationship(
   groups => {
index 10e319b..3e6c03d 100644 (file)
@@ -75,6 +75,9 @@ sub run {
 
 sub data_as_hash {
   my $self = shift;
+
+  $self->data(YAML::Dump($_[0])) if @_;
+
   return {}                        if !$self->data;
   return $self->data               if ref($self->{data}) eq 'HASH';
   return YAML::Load($self->{data}) if !ref($self->{data});
@@ -84,9 +87,10 @@ sub data_as_hash {
 sub set_data {
   my ($self, %data) = @_;
 
-  my $data = YAML::Load($self->data);
-  $data->{$_} = $data{$_} for keys %data;
-  $self->data(YAML::Dump($data));
+  $self->data(YAML::Dump({
+    %{ $self->data_as_hash },
+    %data,
+  }));
 
   $self;
 }
index b530db7..436c9b1 100644 (file)
@@ -9,6 +9,7 @@ use Rose::DB::Object::Helpers ();
 use SL::DB::MetaSetup::DeliveryOrder;
 use SL::DB::Manager::DeliveryOrder;
 use SL::DB::Helper::AttrHTML;
+use SL::DB::Helper::AttrSorted;
 use SL::DB::Helper::FlattenToForm;
 use SL::DB::Helper::LinkedRecords;
 use SL::DB::Helper::TransNumberGenerator;
@@ -31,6 +32,7 @@ __PACKAGE__->meta->add_relationship(orderitems => { type         => 'one to many
 __PACKAGE__->meta->initialize;
 
 __PACKAGE__->attr_html('notes');
+__PACKAGE__->attr_sorted('items');
 
 __PACKAGE__->before_save('_before_save_set_donumber');
 
@@ -49,12 +51,6 @@ sub _before_save_set_donumber {
 sub items { goto &orderitems; }
 sub add_items { goto &add_orderitems; }
 
-sub items_sorted {
-  my ($self) = @_;
-
-  return [ sort {$a->position <=> $b->position } @{ $self->items } ];
-}
-
 sub sales_order {
   my $self   = shift;
   my %params = @_;
@@ -208,11 +204,6 @@ closed and delivered.
 An alias for C<deliver_orer_items> for compatibility with other
 sales/purchase models.
 
-=item C<items_sorted>
-
-Returns the delivery order items sorted by their ID (same order they
-appear in the frontend delivery order masks).
-
 =item C<new_from $source, %params>
 
 Creates a new C<SL::DB::DeliveryOrder> instance and copies as much
index 8783006..fb9e833 100644 (file)
@@ -55,6 +55,8 @@ use SL::DB::Inventory;
 use SL::DB::Invoice;
 use SL::DB::InvoiceItem;
 use SL::DB::Language;
+use SL::DB::Letter;
+use SL::DB::LetterDraft;
 use SL::DB::MakeModel;
 use SL::DB::Note;
 use SL::DB::Object::Hooks;
diff --git a/SL/DB/Helper/AttrSorted.pm b/SL/DB/Helper/AttrSorted.pm
new file mode 100644 (file)
index 0000000..77c5f19
--- /dev/null
@@ -0,0 +1,110 @@
+package SL::DB::Helper::AttrSorted;
+
+use Carp;
+use List::Util qw(max);
+
+use strict;
+
+use parent qw(Exporter);
+our @EXPORT = qw(attr_sorted);
+
+sub attr_sorted {
+  my ($package, @attributes) = @_;
+
+  _make_sorted($package, $_) for @attributes;
+}
+
+sub _make_sorted {
+  my ($package, $attribute) = @_;
+
+  my %params       = ref($attribute) eq 'HASH' ? %{ $attribute } : ( unsorted => $attribute );
+  my $unsorted_sub = $params{unsorted};
+  my $sorted_sub   = $params{sorted}   // $params{unsorted} . '_sorted';
+  my $position_sub = $params{position} // 'position';
+
+  no strict 'refs';
+
+  *{ $package . '::' . $sorted_sub } = sub {
+    my ($self) = @_;
+
+    croak 'not an accessor' if @_ > 1;
+
+    my $next_position = (max map { $_->$position_sub // 0 } @{ $self->$unsorted_sub }) + 1;
+    return [
+      map  { $_->[1] }
+      sort { $a->[0] <=> $b->[0] }
+      map  { [ $_->$position_sub // ($next_position++), $_ ] }
+           @{ $self->$unsorted_sub }
+    ];
+  };
+}
+
+1;
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::DB::Helper::AttrSorted - Attribute helper for sorting to-many
+relationships by a positional attribute
+
+=head1 SYNOPSIS
+
+  # In a Rose model:
+  use SL::DB::Helper::AttrSorted;
+  __PACKAGE__->attr_sorted('items');
+
+  # Use in controller or whereever:
+  my $items = @{ $invoice->items_sorted };
+
+=head1 OVERVIEW
+
+Creates a function that returns a sorted relationship. Requires that
+the linked objects have some kind of positional column.
+
+Items for which no position has been set (e.g. because they haven't
+been saved yet) are sorted last but kept in the order they appear in
+the unsorted list.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<attr_sorted @attributes>
+
+Package method. Call with the names of the attributes for which the
+helper methods should be created. Each attribute name can be either a
+scalar or a hash reference if you need custom options.
+
+If it's a hash reference then the following keys are supported:
+
+=over 2
+
+=item * C<unsorted> is the name of the relationship accessor that
+returns the list to be sorted. This is required, and if only a scalar
+is given instead of a hash reference then that scalar value is
+interpreted as C<unsorted>.
+
+=item * C<sorted> is the name of the new function to create. It
+defaults to the unsorted name postfixed with C<_sorted>.
+
+=item * C<position> must be a function name to be called on the
+objects to be sorted. It is supposed to return either C<undef> (no
+position has been set yet) or a numeric value. Defaults to C<position>.
+
+=back
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
index c29cc0a..31ae7b6 100644 (file)
@@ -138,6 +138,8 @@ my %kivitendo_package_names = (
   inventory                      => 'inventory',
   invoice                        => 'invoice_item',
   language                       => 'language',
+  letter                         => 'letter',
+  letter_draft                   => 'letter_draft',
   makemodel                      => 'make_model',
   notes                          => 'note',
   orderitems                     => 'order_item',
index 3ed6cd3..065d2b3 100644 (file)
@@ -1,6 +1,3 @@
-# This file has been auto-generated only because it didn't exist.
-# Feel free to modify it at will; it will not be overwritten automatically.
-
 package SL::DB::Invoice;
 
 use strict;
@@ -13,6 +10,7 @@ use SL::DB::MetaSetup::Invoice;
 use SL::DB::Manager::Invoice;
 use SL::DB::Helper::Payment qw(:ALL);
 use SL::DB::Helper::AttrHTML;
+use SL::DB::Helper::AttrSorted;
 use SL::DB::Helper::FlattenToForm;
 use SL::DB::Helper::LinkedRecords;
 use SL::DB::Helper::PriceTaxCalculator;
@@ -61,6 +59,7 @@ __PACKAGE__->meta->add_relationship(
 __PACKAGE__->meta->initialize;
 
 __PACKAGE__->attr_html('notes');
+__PACKAGE__->attr_sorted('items');
 
 __PACKAGE__->before_save('_before_save_set_invnumber');
 
@@ -79,12 +78,6 @@ sub _before_save_set_invnumber {
 sub items { goto &invoiceitems; }
 sub add_items { goto &add_invoiceitems; }
 
-sub items_sorted {
-  my ($self) = @_;
-
-  return [ sort {$a->position <=> $b->position } @{ $self->items } ];
-}
-
 sub is_sales {
   # For compatibility with Order, DeliveryOrder
   croak 'not an accessor' if @_ > 1;
diff --git a/SL/DB/Letter.pm b/SL/DB/Letter.pm
new file mode 100644 (file)
index 0000000..08657bd
--- /dev/null
@@ -0,0 +1,15 @@
+# This file has been auto-generated only because it didn't exist.
+# Feel free to modify it at will; it will not be overwritten automatically.
+
+package SL::DB::Letter;
+
+use strict;
+
+use SL::DB::MetaSetup::Letter;
+
+__PACKAGE__->meta->initialize;
+
+# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
+__PACKAGE__->meta->make_manager_class;
+
+1;
diff --git a/SL/DB/LetterDraft.pm b/SL/DB/LetterDraft.pm
new file mode 100644 (file)
index 0000000..eb4a4b6
--- /dev/null
@@ -0,0 +1,15 @@
+# This file has been auto-generated only because it didn't exist.
+# Feel free to modify it at will; it will not be overwritten automatically.
+
+package SL::DB::LetterDraft;
+
+use strict;
+
+use SL::DB::MetaSetup::LetterDraft;
+
+__PACKAGE__->meta->initialize;
+
+# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
+__PACKAGE__->meta->make_manager_class;
+
+1;
index e566089..5671a4b 100644 (file)
@@ -64,6 +64,7 @@ __PACKAGE__->meta->columns(
   is_transfer_out                           => { type => 'boolean', default => 'false', not_null => 1 },
   itime                                     => { type => 'timestamp', default => 'now()' },
   language_id                               => { type => 'integer' },
+  letternumber                              => { type => 'integer' },
   max_future_booking_interval               => { type => 'integer', default => 360 },
   mtime                                     => { type => 'timestamp' },
   normalize_part_descriptions               => { type => 'boolean', default => 'true' },
diff --git a/SL/DB/MetaSetup/Letter.pm b/SL/DB/MetaSetup/Letter.pm
new file mode 100644 (file)
index 0000000..a04c4f1
--- /dev/null
@@ -0,0 +1,59 @@
+# This file has been auto-generated. Do not modify it; it will be overwritten
+# by rose_auto_create_model.pl automatically.
+package SL::DB::Letter;
+
+use strict;
+
+use base qw(SL::DB::Object);
+
+__PACKAGE__->meta->table('letter');
+
+__PACKAGE__->meta->columns(
+  id                => { type => 'integer', not_null => 1, sequence => 'id' },
+  vc_id             => { type => 'integer', not_null => 1 },
+  rcv_name          => { type => 'text' },
+  rcv_contact       => { type => 'text' },
+  rcv_address       => { type => 'text' },
+  rcv_countrycode   => { type => 'text' },
+  rcv_zipcode       => { type => 'text' },
+  rcv_city          => { type => 'text' },
+  letternumber      => { type => 'text' },
+  jobnumber         => { type => 'text' },
+  text_created_for  => { type => 'text' },
+  date              => { type => 'text' },
+  subject           => { type => 'text' },
+  greeting          => { type => 'text' },
+  body              => { type => 'text' },
+  close             => { type => 'text' },
+  company_name      => { type => 'text' },
+  employee_id       => { type => 'integer' },
+  employee_position => { type => 'text' },
+  salesman_id       => { type => 'integer' },
+  salesman_position => { type => 'text' },
+  itime             => { type => 'timestamp', default => 'now()' },
+  mtime             => { type => 'timestamp' },
+  rcv_country       => { type => 'text' },
+  page_created_for  => { type => 'text' },
+  cp_id             => { type => 'integer' },
+);
+
+__PACKAGE__->meta->primary_key_columns([ 'id' ]);
+
+__PACKAGE__->meta->allow_inline_column_values(1);
+
+__PACKAGE__->meta->foreign_keys(
+  employee => {
+    class       => 'SL::DB::Employee',
+    key_columns => { employee_id => 'id' },
+  },
+
+  salesman => {
+    class       => 'SL::DB::Employee',
+    key_columns => { salesman_id => 'id' },
+  },
+);
+
+# __PACKAGE__->meta->initialize;
+
+1;
+;
diff --git a/SL/DB/MetaSetup/LetterDraft.pm b/SL/DB/MetaSetup/LetterDraft.pm
new file mode 100644 (file)
index 0000000..6b450d0
--- /dev/null
@@ -0,0 +1,64 @@
+# This file has been auto-generated. Do not modify it; it will be overwritten
+# by rose_auto_create_model.pl automatically.
+package SL::DB::LetterDraft;
+
+use strict;
+
+use base qw(SL::DB::Object);
+
+__PACKAGE__->meta->table('letter_draft');
+
+__PACKAGE__->meta->columns(
+  body              => { type => 'text' },
+  close             => { type => 'text' },
+  company_name      => { type => 'text' },
+  cp_id             => { type => 'integer' },
+  date              => { type => 'date' },
+  employee_id       => { type => 'integer' },
+  employee_position => { type => 'text' },
+  greeting          => { type => 'text' },
+  id                => { type => 'integer', not_null => 1, sequence => 'id' },
+  intnotes          => { type => 'text' },
+  itime             => { type => 'timestamp', default => 'now()' },
+  jobnumber         => { type => 'text' },
+  letternumber      => { type => 'text' },
+  mtime             => { type => 'timestamp' },
+  page_created_for  => { type => 'text' },
+  rcv_address       => { type => 'text' },
+  rcv_city          => { type => 'text' },
+  rcv_contact       => { type => 'text' },
+  rcv_country       => { type => 'text' },
+  rcv_countrycode   => { type => 'text' },
+  rcv_name          => { type => 'text' },
+  rcv_zipcode       => { type => 'text' },
+  reference         => { type => 'text' },
+  salesman_id       => { type => 'integer' },
+  salesman_position => { type => 'text' },
+  subject           => { type => 'text' },
+  text_created_for  => { type => 'text' },
+  vc_id             => { type => 'integer', not_null => 1 },
+);
+
+__PACKAGE__->meta->primary_key_columns([ 'id' ]);
+
+__PACKAGE__->meta->allow_inline_column_values(1);
+
+__PACKAGE__->meta->foreign_keys(
+  contact => {
+    class       => 'SL::DB::Contact',
+    key_columns => { cp_id => 'cp_id' },
+  },
+
+  employee => {
+    class       => 'SL::DB::Employee',
+    key_columns => { employee_id => 'id' },
+  },
+
+  salesman => {
+    class       => 'SL::DB::Employee',
+    key_columns => { salesman_id => 'id' },
+  },
+);
+
+1;
+;
index 48e2be6..f73e32c 100644 (file)
@@ -10,6 +10,7 @@ use List::Util qw(max);
 use SL::DB::MetaSetup::Order;
 use SL::DB::Manager::Order;
 use SL::DB::Helper::AttrHTML;
+use SL::DB::Helper::AttrSorted;
 use SL::DB::Helper::FlattenToForm;
 use SL::DB::Helper::LinkedRecords;
 use SL::DB::Helper::PriceTaxCalculator;
@@ -43,6 +44,7 @@ __PACKAGE__->meta->add_relationship(
 __PACKAGE__->meta->initialize;
 
 __PACKAGE__->attr_html('notes');
+__PACKAGE__->attr_sorted('items');
 
 __PACKAGE__->before_save('_before_save_set_ord_quo_number');
 
@@ -66,12 +68,6 @@ sub _before_save_set_ord_quo_number {
 sub items { goto &orderitems; }
 sub add_items { goto &add_orderitems; }
 
-sub items_sorted {
-  my ($self) = @_;
-
-  return [ sort {$a->position <=> $b->position } @{ $self->items } ];
-}
-
 sub type {
   my $self = shift;
 
index e33b3a3..70b5ab4 100644 (file)
@@ -96,7 +96,8 @@ sub validate {
   my @errors;
   push @errors, $::locale->text('The name must not be empty.')              if !$self->name;
   push @errors, $::locale->text('Price or discount must not be zero.')      if !$self->price && !$self->discount && !$self->reduction;
-  push @errors, $::locale->text('Pirce rules must have at least one rule.') if !@{[ $self->items ]};
+  push @errors, $::locale->text('Price rules must have at least one rule.') if !@{[ $self->items ]};
+  push @errors, $_->validate                                                for $self->items;
 
   return @errors;
 }
index 3fc39df..8aa71da 100644 (file)
@@ -132,4 +132,15 @@ sub full_description {
   : do { die "unknown type $type" }
 }
 
+sub validate {
+  my ($self) = @_;
+
+  my @errors;
+  push @errors, t8('Rule for part must not be empty')     if $self->type eq 'part'     && !$self->value_int;
+  push @errors, t8('Rule for customer must not be empty') if $self->type eq 'customer' && !$self->value_int;
+  push @errors, t8('Rule for vendor must not be empty')   if $self->type eq 'vendor'   && !$self->value_int;
+
+  return @errors;
+}
+
 1;
index ed0523b..ab1bd49 100644 (file)
@@ -7,6 +7,7 @@ use Carp;
 use SL::DB::MetaSetup::PurchaseInvoice;
 use SL::DB::Manager::PurchaseInvoice;
 use SL::DB::Helper::AttrHTML;
+use SL::DB::Helper::AttrSorted;
 use SL::DB::Helper::LinkedRecords;
 use SL::DB::Helper::Payment qw(:ALL);
 use SL::Locale::String qw(t8);
@@ -45,16 +46,11 @@ __PACKAGE__->meta->add_relationship(
 __PACKAGE__->meta->initialize;
 
 __PACKAGE__->attr_html('notes');
+__PACKAGE__->attr_sorted('items');
 
 sub items { goto &invoiceitems; }
 sub add_items { goto &add_invoiceitems; }
 
-sub items_sorted {
-  my ($self) = @_;
-
-  return [ sort {$a->position <=> $b->position } @{ $self->items } ];
-}
-
 sub is_sales {
   # For compatibility with Order, DeliveryOrder
   croak 'not an accessor' if @_ > 1;
index c9289e4..5520083 100644 (file)
--- a/SL/DN.pm
+++ b/SL/DN.pm
@@ -211,7 +211,7 @@ sub create_invoice_for_fees {
   $query =
     qq|INSERT INTO ar (id,          invnumber, transdate, gldate, customer_id,
                        taxincluded, amount,    netamount, paid,   duedate,
-                       invoice,     currency_id,      notes,
+                       invoice,     currency_id, taxzone_id,      notes,
                        employee_id)
        VALUES (
          ?,                     -- id
@@ -232,6 +232,14 @@ sub create_invoice_for_fees {
          (SELECT duedate FROM dunning WHERE dunning_id = ? LIMIT 1),
          'f',                   -- invoice
          (SELECT id FROM currencies WHERE name = ?), -- curr
+         --taxzone_id:
+         (SELECT taxzone_id FROM customer WHERE id =
+          (SELECT ar.customer_id
+           FROM dunning dn
+           LEFT JOIN ar ON (dn.trans_id = ar.id)
+           WHERE dn.dunning_id = ?
+           LIMIT 1)
+         ),
          ?,                     -- notes
          -- employee_id:
          (SELECT id FROM employee WHERE login = ?)
@@ -243,6 +251,7 @@ sub create_invoice_for_fees {
              $amount,
              $dunning_id,       # duedate
              $curr,             # default currency
+             $dunning_id,       # taxzone_id
              sprintf($main::locale->text('Automatically created invoice for fee and interest for dunning %s'), $dunning_id), # notes
              $::myconfig{login});   # employee_id
   do_query($form, $dbh, $query, @values);
index 80b1ae3..e764ac6 100644 (file)
--- a/SL/DO.pm
+++ b/SL/DO.pm
@@ -67,9 +67,10 @@ sub transactions {
   my $query =
     qq|SELECT dord.id, dord.donumber, dord.ordnumber, dord.cusordnumber,
          dord.transdate, dord.reqdate,
-         ct.${vc}number, ct.name, dord.${vc}_id, dord.globalproject_id,
+         ct.${vc}number, ct.name, ct.business_id,
+         dord.${vc}_id, dord.globalproject_id,
          dord.closed, dord.delivered, dord.shippingpoint, dord.shipvia,
-         dord.transaction_description,
+         dord.transaction_description, dord.itime::DATE AS insertdate,
          pr.projectnumber AS globalprojectnumber,
          dep.description AS department,
          e.name AS employee,
@@ -98,6 +99,11 @@ sub transactions {
     push @values, conv_i($form->{project_id}), conv_i($form->{project_id});
   }
 
+  if ($form->{"business_id"}) {
+    push @where,  qq|ct.business_id = ?|;
+    push @values, conv_i($form->{"business_id"});
+  }
+
   if ($form->{"${vc}_id"}) {
     push @where,  qq|dord.${vc}_id = ?|;
     push @values, $form->{"${vc}_id"};
@@ -163,6 +169,16 @@ sub transactions {
     push @values, conv_date($form->{reqdateto});
   }
 
+  if($form->{insertdatefrom}) {
+    push @where, qq|dord.itime::DATE >= ?|;
+    push@values, conv_date($form->{insertdatefrom});
+  }
+
+  if($form->{insertdateto}) {
+    push @where, qq|dord.itime::DATE <= ?|;
+    push @values, conv_date($form->{insertdateto});
+  }
+
   if (@where) {
     $query .= " WHERE " . join(" AND ", map { "($_)" } @where);
   }
@@ -179,6 +195,7 @@ sub transactions {
     "shipvia"                 => "dord.shipvia",
     "transaction_description" => "dord.transaction_description",
     "department"              => "lower(dep.description)",
+    "insertdate"              => "dord.itime",
   );
 
   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
@@ -673,7 +690,7 @@ sub retrieve {
          dord.shipto_id,
          dord.globalproject_id, dord.delivered, dord.transaction_description,
          dord.taxzone_id, dord.taxincluded, dord.terms, (SELECT cu.name FROM currencies cu WHERE cu.id=dord.currency_id) AS currency,
-         dord.delivery_term_id
+         dord.delivery_term_id, dord.itime::DATE AS insertdate
        FROM delivery_orders dord
        JOIN ${vc} cv ON (dord.${vc}_id = cv.id)
        LEFT JOIN employee e ON (dord.employee_id = e.id)
index 9ef6ef3..06fd3cf 100644 (file)
@@ -23,7 +23,6 @@ sub handle {
   }
 
   $::request->{layout} = SL::Layout::Dispatcher->new(style => 'admin');
-  $::request->layout->no_menu(1);
   $::auth->delete_session_value('admin_password');
   $::auth->punish_wrong_login;
   SL::Dispatcher::show_error('admin/adminlogin', 'password');
index 27af46a..e25e13e 100644 (file)
--- a/SL/IC.pm
+++ b/SL/IC.pm
@@ -526,6 +526,20 @@ sub save {
                               variables     => $form,
                               save_validity => 1);
 
+  # Delete saved custom variable values for configs that have been
+  # marked invalid for this part.
+  $query = <<SQL;
+    DELETE FROM custom_variables
+    WHERE (config_id IN (
+        SELECT val.config_id
+        FROM custom_variables_validity val
+        LEFT JOIN custom_variable_configs val_cfg ON (val.config_id = val_cfg.id)
+        WHERE (val_cfg.module = 'IC')
+          AND (val.trans_id   = ?)))
+      AND (trans_id = ?)
+SQL
+  do_query($form, $dbh, $query, ($form->{id}) x 2);
+
   # commit
   my $rc = $dbh->commit;
 
@@ -730,7 +744,7 @@ sub all_parts {
   my @apoe_filters         = qw(transdate);
   my @like_filters         = (@simple_filters, @invoice_oi_filters);
   my @all_columns          = (@simple_filters, @makemodel_filters, @apoe_filters, @project_filters, qw(serialnumber));
-  my @simple_l_switches    = (@all_columns, qw(notes listprice sellprice lastcost priceupdate weight unit rop image));
+  my @simple_l_switches    = (@all_columns, qw(notes listprice sellprice lastcost priceupdate weight unit rop image shop insertdate));
   my @oe_flags             = qw(bought sold onorder ordered rfq quoted);
   my @qsooqr_flags         = qw(invnumber ordnumber quonumber trans_id name module qty);
   my @deliverydate_flags   = qw(deliverydate);
@@ -806,10 +820,12 @@ sub all_parts {
     'ioi.id'       => 'ioi_id',
     'ioi.ioi'      => 'ioi',
     'projectdescription' => 'projectdescription',
+    'insertdate'   => 'insertdate',
   );
 
   my %real_column = (
     projectdescription => 'description',
+    insertdate         => 'itime::DATE',
   );
 
   if (($form->{searchitems} eq 'assembly') && $form->{l_lastcost}) {
@@ -850,12 +866,37 @@ sub all_parts {
     }
   }
 
+  # special case insertdate
+  if (grep { $form->{$_} } qw(insertdatefrom insertdateto)) {
+    $form->{"l_insertdate"} = 1;
+    push @select_tokens, 'insertdate';
+
+    my $token_builder = $make_token_builder->();
+    my $token = $token_builder->('insertdate');
+
+    for (qw(insertdatefrom insertdateto)) {
+      next unless $form->{$_};
+      push @where_tokens, sprintf "$token %s ?", /from$/ ? '>=' : '<=';
+      push @bind_vars,    $form->{$_};
+    }
+  }
+
   if ($form->{"partsgroup_id"}) {
     $form->{"l_partsgroup"} = '1'; # show the column
     push @where_tokens, "pg.id = ?";
     push @bind_vars, $form->{"partsgroup_id"};
   }
 
+  if ($form->{shop} ne '') {
+    $form->{l_shop} = '1'; # show the column
+    if ($form->{shop} eq '0' || $form->{shop} eq 'f') {
+      push @where_tokens, 'NOT p.shop';
+      $form->{shop} = 'f';
+    } else {
+      push @where_tokens, 'p.shop';
+    }
+  }
+
   foreach (@like_filters) {
     next unless $form->{$_};
     $form->{"l_$_"} = '1'; # show the column
@@ -973,7 +1014,7 @@ sub all_parts {
 
   my $token_builder = $make_token_builder->(\%joins_needed);
 
-  my @sort_cols    = (@simple_filters, qw(id priceupdate onhand invnumber ordnumber quonumber name serialnumber soldtotal deliverydate));
+  my @sort_cols    = (@simple_filters, qw(id priceupdate onhand invnumber ordnumber quonumber name serialnumber soldtotal deliverydate insertdate shop));
      $form->{sort} = 'id' unless grep { $form->{"l_$_"} } grep { $form->{sort} eq $_ } @sort_cols; # sort by id if unknown or invisible column
   my $sort_order   = ($form->{revers} ? ' DESC' : ' ASC');
   my $order_clause = " ORDER BY " . $token_builder->($form->{sort}) . ($form->{revers} ? ' DESC' : ' ASC');
@@ -1031,7 +1072,7 @@ sub all_parts {
   if ($form->{searchitems} eq 'assembly' && $form->{bom}) {
     $query =
       qq|SELECT p.id, p.partnumber, p.description, a.qty AS onhand,
-           p.unit, p.notes,
+           p.unit, p.notes, p.itime::DATE as insertdate,
            p.sellprice, p.listprice, p.lastcost,
            p.rop, p.weight, p.priceupdate,
            p.image, p.drawing, p.microfiche,
index 208d2e4..8bd2f5f 100644 (file)
--- a/SL/IR.pm
+++ b/SL/IR.pm
@@ -1133,7 +1133,7 @@ sub get_vendor {
          v.email, v.cc, v.bcc, v.language_id, v.payment_id, v.delivery_term_id,
          v.street, v.zipcode, v.city, v.country, v.taxzone_id, cu.name AS curr, v.direct_debit,
          $duedate + COALESCE(pt.terms_netto, 0) AS duedate,
-         b.description AS business
+         b.discount AS tradediscount, b.description AS business
        FROM vendor v
        LEFT JOIN business b       ON (b.id = v.business_id)
        LEFT JOIN payment_terms pt ON (v.payment_id = pt.id)
index 8f7c050..b8dd560 100644 (file)
--- a/SL/OE.pm
+++ b/SL/OE.pm
@@ -109,6 +109,7 @@ sub transactions {
     qq|  o.closed, o.delivered, o.quonumber, o.cusordnumber, o.shippingpoint, o.shipvia, | .
     qq|  o.transaction_description, | .
     qq|  o.marge_total, o.marge_percent, | .
+    qq|  o.itime::DATE AS insertdate, | .
     qq|  ex.$rate AS exchangerate, | .
     qq|  pr.projectnumber AS globalprojectnumber, | .
     qq|  e.name AS employee, s.name AS salesman, | .
@@ -230,6 +231,16 @@ SQL
     push(@values, conv_date($form->{reqdateto}));
   }
 
+  if($form->{insertdatefrom}) {
+    $query .= qq| AND o.itime::DATE >= ?|;
+    push(@values, conv_date($form->{insertdatefrom}));
+  }
+
+  if($form->{insertdateto}) {
+    $query .= qq| AND o.itime::DATE <= ?|;
+    push(@values, conv_date($form->{insertdateto}));
+  }
+
   if ($form->{shippingpoint}) {
     $query .= qq| AND o.shippingpoint ILIKE ?|;
     push(@values, '%' . $form->{shippingpoint} . '%');
@@ -270,8 +281,17 @@ SQL
     push @values, conv_date($form->{expected_billing_date_to});
   }
 
+  my ($cvar_where, @cvar_values) = CVar->build_filter_query('module'         => 'CT',
+                                                            'trans_id_field' => 'ct.id',
+                                                            'filter'         => $form,
+                                                           );
+  if ($cvar_where) {
+    $query .= qq| AND ($cvar_where)|;
+    push @values, @cvar_values;
+  }
+
   my $sortdir   = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
-  my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name")));
+  my $sortorder = join(', ', map { "${_} ${sortdir} " } ("o.id", $form->sort_columns("transdate", $ordnumber, "name"), "o.itime"));
   my %allowed_sort_columns = (
     "transdate"               => "o.transdate",
     "reqdate"                 => "o.reqdate",
@@ -285,10 +305,11 @@ SQL
     "shipvia"                 => "o.shipvia",
     "transaction_description" => "o.transaction_description",
     "shippingpoint"           => "o.shippingpoint",
+    "insertdate"              => "o.itime",
     "taxzone"                 => "tz.description",
   );
   if ($form->{sort} && grep($form->{sort}, keys(%allowed_sort_columns))) {
-    $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}";
+    $sortorder = $allowed_sort_columns{$form->{sort}} . " ${sortdir}"  . ", o.itime ${sortdir}";
   }
   $query .= qq| ORDER by | . $sortorder;
 
@@ -889,8 +910,8 @@ sub retrieve {
            o.closed, o.reqdate, o.quonumber, o.department_id, o.cusordnumber,
            d.description AS department, o.payment_id, o.language_id, o.taxzone_id,
            o.delivery_customer_id, o.delivery_vendor_id, o.proforma, o.shipto_id,
-           o.globalproject_id, o.delivered, o.transaction_description, o.delivery_term_id
-           , o.order_probability, o.expected_billing_date
+           o.globalproject_id, o.delivered, o.transaction_description, o.delivery_term_id,
+           o.itime::DATE AS insertdate, o.order_probability, o.expected_billing_date
          FROM oe o
          JOIN ${vc} cv ON (o.${vc}_id = cv.id)
          LEFT JOIN employee e ON (o.employee_id = e.id)
index 89dffba..c8cdc6c 100644 (file)
@@ -4,6 +4,7 @@ use strict;
 use parent 'SL::DB::Object';
 use Rose::Object::MakeMethods::Generic (
   scalar => [ qw(record_item record) ],
+  'array --get_set_init' => [ qw(all_price_sources) ],
 );
 
 use List::UtilsBy qw(min_by max_by);
@@ -11,12 +12,12 @@ use SL::PriceSource::ALL;
 use SL::PriceSource::Price;
 use SL::Locale::String;
 
-sub all_price_sources {
+sub init_all_price_sources {
   my ($self) = @_;
 
-  map {
+  map {
     $_->new(record_item => $self->record_item, record => $self->record)
-  } SL::PriceSource::ALL->all_enabled_price_sources
+  } SL::PriceSource::ALL->all_enabled_price_sources ]
 }
 
 sub price_from_source {
@@ -71,16 +72,16 @@ together to calculate available prices for a position in a record.
 Each algorithm can access details of the record to realize dependencies on
 part, customer, vendor, date, quantity etc, which was previously not possible.
 
-=head1 BACKGROUND AND PHILOSOPY
+=head1 BACKGROUND AND PHILOSOPHY
 
 sql ledger and subsequently Lx-Office had three prices per part: sellprice,
 listprice and lastcost. At the moment a part is loaded into a record, the
-applicable price is copied and after that free to be changed.
+applicable price is copied and after that it is free to be changed.
 
-Later on additional things joined. Various types of discount, vendor pricelists
+Later on additional things were added. Various types of discount, vendor pricelists
 and the infamous price groups. The problem is not that those didn't work, the
-problem is, that they had to guess to much when to change a price with the
-available price from database, and when to leave the user entered price.
+problem is, that they had to guess too much when to change a price with the
+available price from the database, and when to leave the user entered price.
 
 Unrelated to that, users asked for more ways to store special prices, based on
 qty (block pricing, bulk discount), based on date (special offers), based on
index ca66c5d..fc7b298 100644 (file)
@@ -50,13 +50,14 @@ sub price_from_source {
     )
   }
 
-  if ($business->id != $self->customer_vendor->business->id) {
+  if (!$self->customer_vendor->business ||
+      $business->id != $self->customer_vendor->business->id) {
     return SL::PriceSource::Discount->new(
       discount     => $business->discount,
       spec         => $business->id,
       description  => t8('Business Discount'),
       price_source => $self,
-      invalid      => t8('This discount is only valid for business #1', $business->full_description),
+      invalid      => t8('This discount is only valid for business #1', $business->displayable_name),
     )
   }
 
index a7475ea..ae4592b 100644 (file)
@@ -51,7 +51,7 @@ __END__
 
 =head1 NAME
 
-SL::PriceSource::Discount - contrainer to pass calculated discounts around
+SL::PriceSource::Discount - container to pass calculated discounts around
 
 =head1 SYNOPSIS
 
@@ -75,7 +75,7 @@ SL::PriceSource::Discount - contrainer to pass calculated discounts around
   );
 
   # invalid discount
-  SL::PriceSource::Dicount->new(
+  SL::PriceSource::Discount->new(
     discount     => $original_discount,
     spec         => $original_spec,
     description  => $original_description,
index 8a322c5..2a8adae 100644 (file)
@@ -23,7 +23,7 @@ sub available_prices {
   my $prices = SL::DB::Manager::Price->get_all(
     query        => [ parts_id => $item->parts_id, price => { gt => 0 } ],
     with_objects => 'pricegroup',
-    order_by     => 'pricegroun.id',
+    order_by     => 'pricegroup.id',
   );
 
   return () unless @$prices;
index 94d9bd0..0b09ad6 100644 (file)
@@ -4,7 +4,7 @@ use strict;
 use parent qw(SL::PriceSource::Base);
 
 use SL::DB::Vendor;
-use SL::PriceSource::Price;
+use SL::PriceSource::Discount;
 use SL::Locale::String;
 
 sub name { 'vendor_discount' }
@@ -20,7 +20,7 @@ sub available_discounts {
   return unless $self->record->vendor;
   return unless $self->record->vendor->discount != 0;
 
-  SL::PriceSource::Vendor->new(
+  SL::PriceSource::Discount->new(
     discount     => $self->record->vendor->discount,
     spec         => $self->record->vendor->id,
     description  => t8('Vendor Discount'),
index 2960001..6a024a3 100644 (file)
@@ -842,6 +842,12 @@ sub search {
   $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
   $form->{SHOW_BUSINESS_TYPES} = scalar @{ $form->{ALL_BUSINESS_TYPES} } > 0;
 
+  $form->{CT_CUSTOM_VARIABLES}                  = CVar->get_configs('module' => 'CT');
+  ($form->{CT_CUSTOM_VARIABLES_FILTER_CODE},
+   $form->{CT_CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables'      => $form->{CT_CUSTOM_VARIABLES},
+                                                                              'include_prefix' => 'l_',
+                                                                              'include_value'  => 'Y');
+
   # constants and subs for template
   $form->{vc_keys}   = sub { "$_[0]->{name}--$_[0]->{id}" };
 
@@ -898,8 +904,16 @@ sub ar_transactions {
        datepaid due duedate transaction_description notes salesman employee shippingpoint shipvia
        marge_total marge_percent globalprojectnumber customernumber country ustid taxzone payment_terms charts customertype);
 
+  my $ct_cvar_configs                 = CVar->get_configs('module' => 'CT');
+  my @ct_includeable_custom_variables = grep { $_->{includeable} } @{ $ct_cvar_configs };
+  my @ct_searchable_custom_variables  = grep { $_->{searchable} }  @{ $ct_cvar_configs };
+
+  my %column_defs_cvars = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @ct_includeable_custom_variables;
+  push @columns, map { "cvar_$_->{name}" } @ct_includeable_custom_variables;
+
   my @hidden_variables = map { "l_${_}" } @columns;
   push @hidden_variables, "l_subtotal", qw(open closed customer invnumber ordnumber cusordnumber transaction_description notes project_id transdatefrom transdateto employee_id salesman_id business_id);
+  push @hidden_variables, map { "cvar_$_->{name}" } @ct_searchable_custom_variables;
 
   $href = build_std_url('action=ar_transactions', grep { $form->{$_} } @hidden_variables);
 
@@ -934,6 +948,7 @@ sub ar_transactions {
     'payment_terms'           => { 'text' => $locale->text('Payment Terms'), },
     'charts'                  => { 'text' => $locale->text('Buchungskonto'), },
     'customertype'            => { 'text' => $locale->text('Customer type'), },
+    %column_defs_cvars,
   );
 
   foreach my $name (qw(id transdate duedate invnumber ordnumber cusordnumber name datepaid employee shippingpoint shipvia transaction_description)) {
@@ -953,6 +968,12 @@ sub ar_transactions {
 
   $report->set_sort_indicator($form->{sort}, $form->{sortdir});
 
+  CVar->add_custom_variables_to_report('module'         => 'CT',
+                                       'trans_id_field' => 'customer_id',
+                                       'configs'        => $ct_cvar_configs,
+                                       'column_defs'    => \%column_defs,
+                                       'data'           => $form->{AR});
+
   my @options;
   if ($form->{customer}) {
     push @options, $locale->text('Customer') . " : $form->{customer}";
index 5a2dbd6..ec1ccbf 100644 (file)
@@ -160,11 +160,17 @@ sub list_names {
     }
   }
 
+  if ( $form->{insertdatefrom} or $form->{insertdateto} ) {
+    push @options, $locale->text('Insert Date');
+    push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1) if $form->{insertdatefrom};
+    push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{insertdateto},   1) if $form->{insertdateto};
+  };
+
   my @columns = (
     'id',        'name',    "$form->{db}number",   'contact',   'phone',    'discount',
     'fax',       'email',   'taxnumber',           'street',    'zipcode' , 'city',
     'business',  'payment', 'invnumber', 'ordnumber',           'quonumber', 'salesman',
-    'country'
+    'country',   'insertdate',           'pricegroup'
   );
 
   my @includeable_custom_variables = grep { $_->{includeable} } @{ $cvar_configs };
@@ -194,6 +200,8 @@ sub list_names {
     'salesman'          => { 'text' => $locale->text('Salesman'), },
     'discount'          => { 'text' => $locale->text('Discount'), },
     'payment'           => { 'text' => $locale->text('Payment Terms'), },
+    'insertdate'        => { 'text' => $locale->text('Insert Date'), },
+    'pricegroup'        => { 'text' => $locale->text('Pricegroup'), },
     %column_defs_cvars,
   );
 
@@ -201,7 +209,7 @@ sub list_names {
 
   my @hidden_variables  = ( qw(
       db status obsolete name contact email cp_name addr_street addr_zipcode
-      addr_city addr_country business_id salesman_id
+      addr_city addr_country business_id salesman_id insertdateto insertdatefrom
     ), "$form->{db}number",
     map({ "cvar_$_->{name}" } @searchable_custom_variables),
     map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
index 3f62418..33ab98c 100644 (file)
@@ -369,6 +369,8 @@ sub update_delivery_order {
 
   set_headings($form->{"id"} ? "edit" : "add");
 
+  $form->{insertdate} = SL::DB::DeliveryOrder->new(id => $form->{id})->load->itime_as_date if $form->{id};
+
   $form->{update} = 1;
 
   my $payment_id;
@@ -419,7 +421,7 @@ sub update_delivery_order {
 
       if ($rows > 1) {
 
-        select_item(mode => $mode);
+        select_item(mode => $mode, pre_entered_qty => $form->{"qty_$i"});
         ::end_of_request();
 
       } else {
@@ -451,6 +453,7 @@ sub update_delivery_order {
         $form->{"sellprice_$i"}          = $form->format_amount(\%myconfig, $form->{"sellprice_$i"});
         $form->{"lastcost_$i"}           = $form->format_amount(\%myconfig, $form->{"lastcost_$i"});
         $form->{"qty_$i"}                = $form->format_amount(\%myconfig, $form->{"qty_$i"});
+        $form->{"discount_$i"}           = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
       }
 
       display_form();
@@ -489,10 +492,11 @@ sub search {
 
   $form->{vc} = $form->{type} eq 'purchase_delivery_order' ? 'vendor' : 'customer';
 
-  $form->get_lists("projects"     => { "key" => "ALL_PROJECTS",
-                                       "all" => 1 },
-                   "departments"  => "ALL_DEPARTMENTS",
-                   "$form->{vc}s" => "ALL_VC");
+  $form->get_lists("projects"       => { "key" => "ALL_PROJECTS",
+                                         "all" => 1 },
+                   "departments"    => "ALL_DEPARTMENTS",
+                   "$form->{vc}s"   => "ALL_VC",
+                   "business_types" => "ALL_BUSINESS_TYPES");
   $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
 
   $form->{SHOW_VC_DROP_DOWN} =  $myconfig{vclimit} > scalar @{ $form->{ALL_VC} };
@@ -532,6 +536,7 @@ sub orders {
     shipvia                 globalprojectnumber
     transaction_description department
     open                    delivered
+    insertdate
   );
 
   $form->{l_open}      = $form->{l_closed} = "Y" if ($form->{open}      && $form->{closed});
@@ -546,7 +551,8 @@ sub orders {
   my @hidden_variables = map { "l_${_}" } @columns;
   push @hidden_variables, $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered donumber ordnumber serialnumber cusordnumber
                                           transaction_description transdatefrom transdateto reqdatefrom reqdateto
-                                          type vc employee_id salesman_id project_id);
+                                          type vc employee_id salesman_id project_id
+                                          insertdatefrom insertdateto business_id);
 
   my $href = build_std_url('action=orders', grep { $form->{$_} } @hidden_variables);
 
@@ -568,9 +574,10 @@ sub orders {
     'open'                    => { 'text' => $locale->text('Open'), },
     'delivered'               => { 'text' => $locale->text('Delivered'), },
     'department'              => { 'text' => $locale->text('Department'), },
+    'insertdate'              => { 'text' => $locale->text('Insert Date'), },
   );
 
-  foreach my $name (qw(id transdate reqdate donumber ordnumber name employee salesman shipvia transaction_description department)) {
+  foreach my $name (qw(id transdate reqdate donumber ordnumber name employee salesman shipvia transaction_description department insertdate)) {
     my $sortdir                 = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
     $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
   }
@@ -608,6 +615,10 @@ sub orders {
     push @options, $locale->text('Order Number') . " : $form->{ordnumber}";
   }
   push @options, $locale->text('Serial Number') . " : $form->{serialnumber}" if $form->{serialnumber};
+  if ($form->{business_id}) {
+    my $vc_type_label = $form->{vc} eq 'customer' ? $locale->text('Customer type') : $locale->text('Vendor type');
+    push @options, $vc_type_label . " : " . SL::DB::Business->new(id => $form->{business_id})->load->description;
+  }
   if ($form->{transaction_description}) {
     push @options, $locale->text('Transaction description') . " : $form->{transaction_description}";
   }
@@ -621,6 +632,11 @@ sub orders {
     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1)       if $form->{reqdatefrom};
     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{reqdateto},   1)       if $form->{reqdateto};
   };
+  if ( $form->{insertdatefrom} or $form->{insertdateto} ) {
+    push @options, $locale->text('Insert Date');
+    push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1)    if $form->{insertdatefrom};
+    push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{insertdateto},   1)    if $form->{insertdateto};
+  };
   if ($form->{open}) {
     push @options, $locale->text('Open');
   }
index 76f0869..f83d42a 100644 (file)
@@ -1023,6 +1023,7 @@ sub generate_report {
     'drawing'            => { 'text' => $locale->text('Drawing'), },
     'ean'                => { 'text' => $locale->text('EAN'), },
     'image'              => { 'text' => $locale->text('Image'), },
+    'insertdate'         => { 'text' => $locale->text('Insert Date'), },
     'invnumber'          => { 'text' => $locale->text('Invoice Number'), },
     'lastcost'           => { 'text' => $locale->text('Last Cost'), },
     'linetotallastcost'  => { 'text' => $locale->text('Extended'), },
@@ -1045,6 +1046,7 @@ sub generate_report {
     'transdate'          => { 'text' => $locale->text('Transdate'), },
     'unit'               => { 'text' => $locale->text('Unit'), },
     'weight'             => { 'text' => $locale->text('Weight'), },
+    'shop'               => { 'text' => $locale->text('Shopartikel'), },
     'projectnumber'      => { 'text' => $locale->text('Project Number'), },
     'projectdescription' => { 'text' => $locale->text('Project Description'), },
   );
@@ -1129,11 +1131,13 @@ sub generate_report {
     microfiche    => $locale->text('Microfiche')       . ": '$form->{microfiche}'",
     l_soldtotal   => $locale->text('Qty in Selected Records'),
     ean           => $locale->text('EAN')              . ": '$form->{ean}'",
+    insertdatefrom => $locale->text('Insert Date') . ": " . $locale->text('From')       . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1),
+    insertdateto   => $locale->text('Insert Date') . ": " . $locale->text('To (time)')  . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1),
   );
 
   my @itemstatus_keys = qw(active obsolete orphaned onhand short);
   my @callback_keys   = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
-                           drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto ean);
+                           drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto insertdatefrom insertdateto ean shop);
 
   # calculate dependencies
   for (@itemstatus_keys, @callback_keys) {
@@ -1219,6 +1223,7 @@ sub generate_report {
     linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
     priceupdate weight image drawing microfiche invnumber ordnumber quonumber
     transdate name serialnumber deliverydate ean projectnumber projectdescription
+    insertdate shop
   );
 
   my $pricegroups = SL::DB::Manager::Pricegroup->get_all(sort => 'id');
@@ -1243,10 +1248,10 @@ sub generate_report {
 
   %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
   map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
-  map { $column_defs{$_}->{align}   = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal), @pricegroup_columns;
+  map { $column_defs{$_}->{align}   = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal shop), @pricegroup_columns;
 
   my @hidden_variables = (
-    qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups),
+    qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups insertdatefrom insertdateto),
     @itemstatus_keys,
     @callback_keys,
     map({ "cvar_$_->{name}" } @searchable_custom_variables),
@@ -1256,7 +1261,7 @@ sub generate_report {
 
   my $callback         = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
 
-  my @sort_full        = qw(partnumber description onhand soldtotal deliverydate);
+  my @sort_full        = qw(partnumber description onhand soldtotal deliverydate insertdate shop);
   my @sort_no_revers   = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
 
   foreach my $col (@sort_full) {
@@ -1350,6 +1355,11 @@ sub generate_report {
 
     $row->{weight}->{data} .= ' ' . $defaults->{weightunit};
 
+    # 'yes' and 'no' for boolean value shop
+    if ($form->{l_shop}) {
+      $row->{shop}{data} = $row->{shop}{data}? $::locale->text('yes') : $::locale->text('no');
+    }
+
     if (!$ref->{assemblyitem}) {
       foreach my $col (@subtotal_columns) {
         $totals{$col}    += $soldtotal * $ref->{$col};
@@ -1838,7 +1848,7 @@ sub update {
 
         if ($rows > 1) {
           $form->{makemodel_rows}--;
-          select_item(mode => 'IC');
+          select_item(mode => 'IC', pre_entered_qty => $form->parse_amount(\%myconfig, $form->{"qty_$i"}));
           ::end_of_request();
         } else {
           map { $form->{item_list}[$i]{$_} =~ s/\"/&quot;/g }
index e584575..74784e6 100644 (file)
@@ -497,8 +497,8 @@ sub select_item {
   $main::lxdebug->enter_sub();
 
   my %params = @_;
-  my $mode   = $params{mode} || croak "Missing parameter 'mode'";
-
+  my $mode            = $params{mode}            || croak "Missing parameter 'mode'";
+  my $pre_entered_qty = $params{pre_entered_qty} || 1;
   _check_io_auth();
 
   my $previous_form = $::auth->save_form_in_session(form => $::form);
@@ -513,11 +513,12 @@ sub select_item {
   # delete action variable
   delete @{$::form}{qw(action item_list)};
 
-  print $::form->parse_html_template('io/select_item', { PREVIOUS_FORM => $previous_form,
-                                                         MODE          => $mode,
-                                                         ITEM_LIST     => \@item_list,
-                                                         IS_ASSEMBLY   => $mode eq 'IC',
-                                                         IS_PURCHASE   => $mode eq 'IS' });
+  print $::form->parse_html_template('io/select_item', { PREVIOUS_FORM   => $previous_form,
+                                                         MODE            => $mode,
+                                                         ITEM_LIST       => \@item_list,
+                                                         IS_ASSEMBLY     => $mode eq 'IC',
+                                                         IS_PURCHASE     => $mode eq 'IS',
+                                                         PRE_ENTERED_QTY => $pre_entered_qty, });
 
   $main::lxdebug->leave_sub();
 }
@@ -536,117 +537,142 @@ sub item_selected {
 
   $::auth->restore_form_from_session($form->{select_item_previous_form} || croak('Missing previous form ID'), form => $form);
 
-  my $mode = delete($form->{select_item_mode}) || croak 'Missing item selection mode';
-  my $id   = delete($form->{select_item_id})   || croak 'Missing item selection ID';
-  my $i    = $form->{ $mode eq 'IC' ? 'assembly_rows' : 'rowcount' };
+  my $mode     = delete($form->{select_item_mode}) || croak 'Missing item selection mode';
+  my $row_key  = $mode eq 'IC' ? 'assembly_rows' : 'rowcount';
+  my $curr_row = $form->{ $row_key };
 
-  if ( $mode eq 'IC' ) {
-    # assembly mode:
-    # the qty variables of the existing assembly items are all still formatted, so we parse them here (1 .. $i-1)
-    # including the qty of the just added part ($i)
-    $form->{"qty_$_"} = $form->parse_amount(\%myconfig, $form->{"qty_$_"}) for (1 .. $i);
-  };
+  my $row = $curr_row;
 
-  $form->{"id_${i}"} = $id;
+  if ($myconfig{item_multiselect}) {
+    foreach (grep(/^select_qty_/, keys(%{ $form }))) {
+      next unless $form->{$_};
+      $_ =~ /^select_qty_(\d+)/;
+      $form->{"id_${row}"}  = $1;
+      $form->{"qty_${row}"} = $form->{$_};
+      $row++;
+    }
+  } else {
+    $form->{"id_${row}"} = delete($form->{select_item_id}) || croak 'Missing item selection ID';
+    $row++;
+  }
+
+  map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
+    qw(sellprice listprice weight);
 
-  if ($mode eq 'IS') {
-    IS->retrieve_item(\%myconfig, \%$form);
-  } elsif ($mode eq 'IR') {
-    IR->retrieve_item(\%myconfig, \%$form);
-  } elsif ($mode eq 'IC') {
-    IC->assembly_item(\%myconfig, \%$form);
+  if ( $mode eq 'IC' ) {
+    # assembly mode:
+    # the qty variables of the existing assembly items are all still formatted, so we parse them here
+    # including the qty of the just added part
+    $form->{"qty_$_"} = $form->parse_amount(\%myconfig, $form->{"qty_$_"}) for (1 .. $row - 1);
   } else {
-    croak "Invalid item selection mode '${mode}'";
+    if ($myconfig{item_multiselect}) {
+      # other modes and multiselection:
+      # parse all newly entered qtys
+      $form->{"qty_$_"} = $form->parse_amount(\%myconfig, $form->{"qty_$_"}) for ($curr_row .. $row - 1);
+    }
   }
 
-  my $new_item = $form->{item_list}->[0] || croak "No item found for mode '${mode}' and ID '${id}'";
+  for my $i ($curr_row .. $row - 1) {
+    $form->{ $row_key } = $i;
 
-  # if there was a price entered, override it
-  my $sellprice;
-  unless ( $mode eq 'IC' ) {
-    $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"});
-  };
+    my $id = $form->{"id_${i}"};
 
-  my @new_fields =
-    qw(id partnumber description sellprice listprice inventory_accno
-       income_accno expense_accno bin unit weight assembly taxaccounts
-       partsgroup formel longdescription not_discountable partnotes lastcost
-       price_factor_id price_factor);
+    delete $form->{item_list};
 
-  my $ic_cvar_configs = CVar->get_configs(module => 'IC');
-  push @new_fields, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
+    if ($mode eq 'IS') {
+      IS->retrieve_item(\%myconfig, \%$form);
+    } elsif ($mode eq 'IR') {
+      IR->retrieve_item(\%myconfig, \%$form);
+    } elsif ($mode eq 'IC') {
+      IC->assembly_item(\%myconfig, \%$form);
+    } else {
+      croak "Invalid item selection mode '${mode}'";
+    }
 
-  map { $form->{"${_}_$i"} = $new_item->{$_} } @new_fields;
+    my $new_item = $form->{item_list}->[0] || croak "No item found for mode '${mode}' and ID '${id}'";
 
-  if (my $record = _make_record()) {
-    my $price_source = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
-    my $best_price   = $price_source->best_price;
+    # if there was a price entered, override it
+    my $sellprice;
+    unless ( $mode eq 'IC' ) {
+      $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"});
+    };
 
-    if ($best_price) {
-      $::form->{"sellprice_$i"}           = $best_price->price;
-      $::form->{"active_price_source_$i"} = $best_price->source;
-    }
+    my @new_fields =
+        qw(id partnumber description sellprice listprice inventory_accno
+           income_accno expense_accno bin unit weight assembly taxaccounts
+           partsgroup formel longdescription not_discountable partnotes lastcost
+           price_factor_id price_factor);
+
+    my $ic_cvar_configs = CVar->get_configs(module => 'IC');
+    push @new_fields, map { "ic_cvar_$_->{name}" } @{ $ic_cvar_configs };
 
-    my $best_discount = $price_source->best_discount;
+    map { $form->{"${_}_$i"} = $new_item->{$_} } @new_fields;
 
-    if ($best_discount) {
-      $::form->{"discount_$i"}               = $best_discount->discount;
-      $::form->{"active_discount_source_$i"} = $best_discount->source;
+    if (my $record = _make_record()) {
+      my $price_source = SL::PriceSource->new(record_item => $record->items->[$i-1], record => $record);
+      my $best_price   = $price_source->best_price;
+
+      if ($best_price) {
+        $::form->{"sellprice_$i"}           = $best_price->price;
+        $::form->{"active_price_source_$i"} = $best_price->source;
+      }
+
+      my $best_discount = $price_source->best_discount;
+
+      if ($best_discount) {
+        $::form->{"discount_$i"}               = $best_discount->discount;
+        $::form->{"active_discount_source_$i"} = $best_discount->source;
+      }
     }
-  }
 
-  $form->{"marge_price_factor_$i"} = $new_item->{price_factor};
+    $form->{"marge_price_factor_$i"} = $new_item->{price_factor};
 
-  if ($form->{"part_payment_id_$i"} ne "") {
-    $form->{payment_id} = $form->{"part_payment_id_$i"};
-  }
+    if ($form->{"part_payment_id_$i"} ne "") {
+      $form->{payment_id} = $form->{"part_payment_id_$i"};
+    }
 
-  my ($dec) = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
-  $dec           = length $dec;
-  my $decimalplaces = ($dec > 2) ? $dec : 2;
+    my ($dec)         = ($form->{"sellprice_$i"} =~ /\.(\d+)/);
+    $dec              = length $dec;
+    my $decimalplaces = ($dec > 2) ? $dec : 2;
 
-  if ($sellprice) {
-    $form->{"sellprice_$i"} = $sellprice;
-  } else {
+    if ($sellprice) {
+      $form->{"sellprice_$i"} = $sellprice;
+    } else {
 
-    # if there is an exchange rate adjust sellprice
-    if (($form->{exchangerate} * 1) != 0) {
-      $form->{"sellprice_$i"} /= $form->{exchangerate};
-      $form->{"sellprice_$i"} =
-        $form->round_amount($form->{"sellprice_$i"}, $decimalplaces);
+      # if there is an exchange rate adjust sellprice
+      if (($form->{exchangerate} * 1) != 0) {
+        $form->{"sellprice_$i"} /= $form->{exchangerate};
+        $form->{"sellprice_$i"} =
+            $form->round_amount($form->{"sellprice_$i"}, $decimalplaces);
+      }
     }
-  }
 
-  map { $form->{$_} = $form->parse_amount(\%myconfig, $form->{$_}) }
-    qw(sellprice listprice weight);
+    # at this stage qty of newly added part needs to be have been parsed
+    $form->{weight}    += ($form->{"weight_$i"} * $form->{"qty_$i"});
 
-  # at this stage qty of newly added part needs to be have been parsed
-  $form->{weight}    += ($form->{"weight_$i"} * $form->{"qty_$i"});
+    if ($form->{"not_discountable_$i"}) {
+      $form->{"discount_$i"} = 0;
+    }
 
-  if ($form->{"not_discountable_$i"}) {
-    $form->{"discount_$i"} = 0;
-  }
+    my $amount =
+        $form->{"sellprice_$i"} * (1 - $form->{"discount_$i"}) * $form->{"qty_$i"};
+    map { $form->{"${_}_base"} += $amount }                         (split / /, $form->{"taxaccounts_$i"});
+    map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /, $form->{"taxaccounts_$i"} if !$form->{taxincluded};
 
-  my $amount =
-    $form->{"sellprice_$i"} * (1 - $form->{"discount_$i"} / 100) *
-    $form->{"qty_$i"};
-  map { $form->{"${_}_base"} += $amount }
-    (split / /, $form->{"taxaccounts_$i"});
-  map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /,
-    $form->{"taxaccounts_$i"}
-    if !$form->{taxincluded};
+    $form->{creditremaining} -= $amount;
 
-  $form->{creditremaining} -= $amount;
+    $form->{"runningnumber_$i"} = $i;
 
-  $form->{"runningnumber_$i"} = $i;
+    # format amounts
+    map {
+      $form->{"${_}_$i"} =
+          $form->format_amount(\%myconfig, $form->{"${_}_$i"}, $decimalplaces)
+    } qw(sellprice listprice lastcost qty) if $form->{item} ne 'assembly';
+    $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0) if $form->{item} ne 'assembly';
 
-  delete $form->{nextsub};
+    delete $form->{nextsub};
 
-  # format amounts
-  map {
-    $form->{"${_}_$i"} =
-      $form->format_amount(\%myconfig, $form->{"${_}_$i"}, $decimalplaces)
-  } qw(sellprice listprice lastcost) if $form->{item} ne 'assembly';
+  }
 
   &display_form;
 
@@ -1352,9 +1378,14 @@ sub print_form {
     $numberfld            = $form->{type} =~ /^sales/ ? 'sdonumber' : 'pdonumber';
     $form->{label}        = $form->{formname} eq 'pick_list' ? $locale->text('Pick List') : $locale->text('Delivery Order');
   }
+  if ($form->{type} =~ /letter/) {
+    undef $due;
+    undef $inv;
+    $form->{label}        = $locale->text('Letter');
+  }
 
   $form->{TEMPLATE_DRIVER_OPTIONS} = { };
-  if (any { $form->{type} eq $_ } qw(sales_quotation sales_order sales_delivery_order invoice request_quotation purchase_order purchase_delivery_order)) {
+  if (any { $form->{type} eq $_ } qw(sales_quotation sales_order sales_delivery_order invoice request_quotation purchase_order purchase_delivery_order credit_note)) {
     $form->{TEMPLATE_DRIVER_OPTIONS}->{variable_content_types} = {
       longdescription => 'html',
       partnotes       => 'html',
@@ -1432,6 +1463,9 @@ sub print_form {
     DO->order_details(\%myconfig, \%$form);
   } elsif ($order) {
     OE->order_details(\%myconfig, \%$form);
+  } elsif ($form->{type} eq 'letter') {
+    # right now, no details are needed
+    # but i do not want to break the bad default (invoice)
   } else {
     IS->invoice_details(\%myconfig, \%$form, $locale);
   }
index 4cd96ca..5bd3548 100644 (file)
@@ -484,12 +484,18 @@ sub update {
 
     my $rows = scalar @{ $form->{item_list} };
 
+    $form->{"discount_$i"}   = $form->parse_amount(\%myconfig, $form->{"discount_$i"}) / 100.0;
+    $form->{"discount_$i"} ||= $form->{vendor_discount};
+
     if ($rows) {
-      $form->{"qty_$i"} = 1 unless $form->parse_amount(\%myconfig, $form->{"qty_$i"});
+      $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
+      if( !$form->{"qty_$i"} ) {
+        $form->{"qty_$i"} = 1;
+      }
 
       if ($rows > 1) {
 
-        select_item(mode => 'IR');
+        select_item(mode => 'IR', pre_entered_qty => $form->{"qty_$i"});
         ::end_of_request();
 
       } else {
@@ -497,8 +503,6 @@ sub update {
         # override sellprice if there is one entered
         my $sellprice = $form->parse_amount(\%myconfig, $form->{"sellprice_$i"});
 
-        # ergaenzung fuer bug 736 Lieferanten-Rabatt auch in Einkaufsrechnungen vorbelegen jb
-        $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{vendor_discount} * 100 );
         map { $form->{item_list}[$i]{$_} =~ s/\"/&quot;/g } qw(partnumber description unit);
         map { $form->{"${_}_$i"} = $form->{item_list}[0]{$_} } keys %{ $form->{item_list}[0] };
 
@@ -529,10 +533,11 @@ sub update {
           $form->{"sellprice_$i"} /= $exchangerate;
         }
 
-        my $amount                   = $form->{"sellprice_$i"} * $form->{"qty_$i"} * (1 - $form->{"discount_$i"} / 100);
+        my $amount                = $form->{"sellprice_$i"} * $form->{"qty_$i"} * (1 - $form->{"discount_$i"});
         $form->{creditremaining} -= $amount;
         $form->{"sellprice_$i"}   = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
         $form->{"qty_$i"}         = $form->format_amount(\%myconfig, $form->{"qty_$i"},       $dec_qty);
+        $form->{"discount_$i"}    = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
       }
 
       &display_form;
index cac93da..a4f9e85 100644 (file)
@@ -554,11 +554,8 @@ sub update {
 
     my $rows = scalar @{ $form->{item_list} };
 
-    # Falls kein Kundenrabatt vorhanden ist, den aktuellen Rabatt nicht mit 0% überschreiben,
-    # da hier der Anwender schon manual einen Wert eingetragen haben könnte (analog zu qty) Bugfix: 1412
-    if ($form->{customer_discount}){
-      $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{customer_discount} * 100);
-    }
+    $form->{"discount_$i"}   = $form->parse_amount(\%myconfig, $form->{"discount_$i"}) / 100.0;
+    $form->{"discount_$i"} ||= $form->{customer_discount};
 
     if ($rows) {
       $form->{"qty_$i"} = $form->parse_amount(\%myconfig, $form->{"qty_$i"});
@@ -568,7 +565,7 @@ sub update {
 
       if ($rows > 1) {
 
-        select_item(mode => 'IS');
+        select_item(mode => 'IS', pre_entered_qty => $form->{"qty_$i"});
         ::end_of_request();
 
       } else {
@@ -609,7 +606,7 @@ sub update {
 
         $form->{"listprice_$i"} /= $exchangerate;
 
-        my $amount = $form->{"sellprice_$i"} * $form->{"qty_$i"} * (1 - $form->{"discount_$i"} / 100);
+        my $amount = $form->{"sellprice_$i"} * $form->{"qty_$i"} * (1 - $form->{"discount_$i"});
         map { $form->{"${_}_base"} = 0 }                                 split / /, $form->{taxaccounts};
         map { $form->{"${_}_base"} += $amount }                          split / /, $form->{"taxaccounts_$i"};
         map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /, $form->{"taxaccounts_$i"} if !$form->{taxincluded};
@@ -618,7 +615,8 @@ sub update {
 
         map { $form->{"${_}_$i"} = $form->format_amount(\%myconfig, $form->{"${_}_$i"}, $decimalplaces) } qw(sellprice lastcost);
 
-        $form->{"qty_$i"} = $form->format_amount(\%myconfig, $form->{"qty_$i"});
+        $form->{"qty_$i"}      = $form->format_amount(\%myconfig, $form->{"qty_$i"});
+        $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
       }
 
       &display_form;
index 18564d3..d9a0742 100755 (executable)
@@ -21,6 +21,8 @@ use SL::DB::Contact;
 use SL::DB::Default;
 use SL::Helper::CreatePDF;
 use SL::Helper::Flash;
+use SL::Common;
+use Cwd;
 require "bin/mozilla/reportgenerator.pl";
 require "bin/mozilla/io.pl";
 require "bin/mozilla/arap.pl";
@@ -371,8 +373,17 @@ sub print_letter {
   );
   my $pdf_file_name;
   eval {
+    # catch LaTeX template not found error
+    my $tex_templates  = $::instance_conf->get_templates . '/letter.tex';
+    die( t8('Please create/copy a template named letter.tex in your client template dir') ) unless (-e $tex_templates);
+
     $pdf_file_name = SL::Helper::CreatePDF->create_pdf(%create_params);
 
+    # set some form defaults for printing webdav copy variables
+    $form->{tmpfile} = $pdf_file_name;
+    $form->{tmpdir} = 'users';
+    $form->{type} = 'letter';
+    $form->{cwd}        = getcwd();
     if ( $::form->{media} eq 'email') {
       my $mail             = Mailer->new;
       my $signature        = $::myconfig{signature};
@@ -384,18 +395,20 @@ sub print_letter {
       $mail->{message}    .=  "\n-- \n$signature";
       $mail->{message}     =~ s/\r//g;
 
+      # copy_file_to_webdav was already done via io.pl -> edit_e_mail
       my $err = $mail->send;
-# TODO
-#       $self
-#           ->js
-#           ->flash($err?'error':'info',
-#                   $err?t8('A mail error occurred: #1', $err):
-#                        t8('The document have been sent to \'#1\'.', $mail->{to}))
-#           ->render($self);
+      # TODO
+      #       $self
+      #           ->js
+      #           ->flash($err?'error':'info',
+      #                   $err?t8('A mail error occurred: #1', $err):
+      #                        t8('The document have been sent to \'#1\'.', $mail->{to}))
+      #           ->render($self);
       return $err?0:1;
     }
 
-    if (!$::form->{printer_id}) {
+    if (!$::form->{printer_id} || $::form->{media} eq 'screen') {
+
       my $file = IO::File->new($pdf_file_name, 'r') || croak("Cannot open file '$pdf_file_name'");
       my $size = -s $pdf_file_name;
       my $content_type    =  'application/pdf';
@@ -408,6 +421,7 @@ sub print_letter {
 
       $::locale->with_raw_io(\*STDOUT, sub { print while <$file> });
       $file->close;
+      Common::copy_file_to_webdav_folder($form) if $::instance_conf->get_webdav_documents;
       unlink $pdf_file_name;
       return 1;
     }
@@ -419,6 +433,7 @@ sub print_letter {
     binmode $out;
     print $out scalar(read_file($pdf_file_name));
     close $out;
+    Common::copy_file_to_webdav_folder($form) if $::instance_conf->get_webdav_documents;
 
     flash_later('info', t8('The documents have been sent to the printer \'#1\'.', $printer->printer_description));
     my $callback = build_std_url('letter.pl', 'action=edit', 'id=' . $letter->{id}, 'printer_id');
@@ -491,6 +506,7 @@ sub e_mail {
   $letter->check_number;
   $letter->save;
 
+  $form->{formname} = "letter";
   $letter->export_to($::form);
 
   $::form->{id} = $letter->{id};
index 147e9d9..93e9f2a 100644 (file)
@@ -631,11 +631,8 @@ sub update {
 
     my $rows = scalar @{ $form->{item_list} };
 
-    # hier ist das problem fuer bug 817 $form->{discount} wird nicht durchgeschliffen
-    # ferner fallunterscheidung fuer verkauf oder einkauf s.a. bug 736 jb 04.05.2009
-    # select discount as vendor_discount from vendor ||
-    # select discount as customer_discount from customer
-    $form->{"discount_$i"} = $form->format_amount(\%myconfig, $form->{"$form->{vc}_discount"} * 100);
+    $form->{"discount_$i"}   = $form->parse_amount(\%myconfig, $form->{"discount_$i"}) / 100.0;
+    $form->{"discount_$i"} ||= $form->{"$form->{vc}_discount"};
 
     $form->{"lastcost_$i"} = $form->parse_amount(\%myconfig, $form->{"lastcost_$i"});
 
@@ -648,7 +645,7 @@ sub update {
 
       if ($rows > 1) {
 
-        select_item(mode => $mode);
+        select_item(mode => $mode, pre_entered_qty => $form->{"qty_$i"});
         ::end_of_request();
 
       } else {
@@ -693,7 +690,7 @@ sub update {
           $form->{"sellprice_$i"} /= $exchangerate;   # if there is an exchange rate adjust sellprice
         }
 
-        my $amount = $form->{"sellprice_$i"} * $form->{"qty_$i"} * (1 - $form->{"discount_$i"} / 100);
+        my $amount = $form->{"sellprice_$i"} * $form->{"qty_$i"} * (1 - $form->{"discount_$i"});
         map { $form->{"${_}_base"} = 0 }                                 split / /, $form->{taxaccounts};
         map { $form->{"${_}_base"} += $amount }                          split / /, $form->{"taxaccounts_$i"};
         map { $amount += ($form->{"${_}_base"} * $form->{"${_}_rate"}) } split / /, $form->{taxaccounts} if !$form->{taxincluded};
@@ -703,6 +700,7 @@ sub update {
         $form->{"sellprice_$i"} = $form->format_amount(\%myconfig, $form->{"sellprice_$i"}, $decimalplaces);
         $form->{"lastcost_$i"}  = $form->format_amount(\%myconfig, $form->{"lastcost_$i"}, $decimalplaces);
         $form->{"qty_$i"}       = $form->format_amount(\%myconfig, $form->{"qty_$i"}, $dec_qty);
+        $form->{"discount_$i"}  = $form->format_amount(\%myconfig, $form->{"discount_$i"} * 100.0);
       }
 
       display_form();
@@ -776,6 +774,12 @@ sub search {
                    "business_types" => "ALL_BUSINESS_TYPES",);
   $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all_sorted(query => [ deleted => 0 ]);
 
+  $form->{CT_CUSTOM_VARIABLES}                  = CVar->get_configs('module' => 'CT');
+  ($form->{CT_CUSTOM_VARIABLES_FILTER_CODE},
+   $form->{CT_CUSTOM_VARIABLES_INCLUSION_CODE}) = CVar->render_search_options('variables'      => $form->{CT_CUSTOM_VARIABLES},
+                                                                              'include_prefix' => 'l_',
+                                                                              'include_value'  => 'Y');
+
   # constants and subs for template
   $form->{vc_keys}         = sub { "$_[0]->{name}--$_[0]->{id}" };
 
@@ -847,7 +851,7 @@ sub orders {
     "marge_total",             "marge_percent",
     "vcnumber",                "ustid",
     "country",                 "shippingpoint",
-    "taxzone",
+    "taxzone",                 "insertdate",
     "order_probability",       "expected_billing_date", "expected_netamount",
   );
 
@@ -885,12 +889,20 @@ sub orders {
 
   my $report = SL::ReportGenerator->new(\%myconfig, $form);
 
+  my $ct_cvar_configs = CVar->get_configs('module' => 'CT');
+  my @ct_includeable_custom_variables = grep { $_->{includeable} } @{ $ct_cvar_configs };
+  my @ct_searchable_custom_variables  = grep { $_->{searchable} }  @{ $ct_cvar_configs };
+
+  my %column_defs_cvars            = map { +"cvar_$_->{name}" => { 'text' => $_->{description} } } @ct_includeable_custom_variables;
+  push @columns, map { "cvar_$_->{name}" } @ct_includeable_custom_variables;
+
   my @hidden_variables = map { "l_${_}" } @columns;
   push @hidden_variables, "l_subtotal", $form->{vc}, qw(l_closed l_notdelivered open closed delivered notdelivered ordnumber quonumber cusordnumber
                                                         transaction_description transdatefrom transdateto type vc employee_id salesman_id
                                                         reqdatefrom reqdateto projectnumber project_id periodic_invoices_active periodic_invoices_inactive
-                                                        business_id shippingpoint taxzone_id reqdate_unset_or_old
+                                                        business_id shippingpoint taxzone_id reqdate_unset_or_old insertdatefrom insertdateto
                                                         order_probability_op order_probability_value expected_billing_date_from expected_billing_date_to);
+  push @hidden_variables, map { "cvar_$_->{name}" } @ct_searchable_custom_variables;
 
   my   @keys_for_url = grep { $form->{$_} } @hidden_variables;
   push @keys_for_url, 'taxzone_id' if $form->{taxzone_id} ne ''; # taxzone_id could be 0
@@ -928,12 +940,14 @@ sub orders {
     'periodic_invoices'       => { 'text' => $locale->text('Per. Inv.'), },
     'shippingpoint'           => { 'text' => $locale->text('Shipping Point'), },
     'taxzone'                 => { 'text' => $locale->text('Steuersatz'), },
+    'insertdate'              => { 'text' => $locale->text('Insert Date'), },
     'order_probability'       => { 'text' => $locale->text('Order probability'), },
     'expected_billing_date'   => { 'text' => $locale->text('Exp. bill. date'), },
     'expected_netamount'      => { 'text' => $locale->text('Exp. netamount'), },
+    %column_defs_cvars,
   );
 
-  foreach my $name (qw(id transdate reqdate quonumber ordnumber cusordnumber name employee salesman shipvia transaction_description shippingpoint taxzone)) {
+  foreach my $name (qw(id transdate reqdate quonumber ordnumber cusordnumber name employee salesman shipvia transaction_description shippingpoint taxzone insertdate)) {
     my $sortdir                 = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
     $column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
   }
@@ -949,6 +963,12 @@ sub orders {
   $report->set_export_options('orders', @hidden_variables, qw(sort sortdir));
   $report->set_sort_indicator($form->{sort}, $form->{sortdir});
 
+  CVar->add_custom_variables_to_report('module'         => 'CT',
+                                       'trans_id_field' => "$form->{vc}_id",
+                                       'configs'        => $ct_cvar_configs,
+                                       'column_defs'    => \%column_defs,
+                                       'data'           => $form->{OE});
+
   my @options;
   my ($department) = split m/--/, $form->{department};
 
@@ -971,6 +991,11 @@ sub orders {
     push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{reqdatefrom}, 1)       if $form->{reqdatefrom};
     push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{reqdateto},   1)       if $form->{reqdateto};
   };
+  if ( $form->{insertdatefrom} or $form->{insertdateto} ) {
+    push @options, $locale->text('Insert Date');
+    push @options, $locale->text('From') . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1)    if $form->{insertdatefrom};
+    push @options, $locale->text('Bis')  . " " . $locale->date(\%myconfig, $form->{insertdateto},   1)    if $form->{insertdateto};
+  };
   push @options, $locale->text('Open')                                                                    if $form->{open};
   push @options, $locale->text('Closed')                                                                  if $form->{closed};
   push @options, $locale->text('Delivery Order created')                                                               if $form->{delivered};
index fb8ec75..2594ef6 100644 (file)
@@ -42,7 +42,7 @@ a.cti_call_action {
   position: relative;
   top: 2px;
   vertical-align: center;
-  background-image: url(../../image/icons/16x16/phone.png);
+  background-image: url(../image/icons/16x16/phone.png);
   background-repeat: no-repeat;
 }
 
index 5c9aaab..8df983b 100644 (file)
@@ -4,7 +4,6 @@
 
 2015-0x-xx - Release 3.2.2-unstable
 
-
 Größere neue Features:
 
 Bankerweiterung und Skontobehandlung
@@ -37,9 +36,45 @@ __Es wurde ein neues Recht "Bankbewegungen" eingeführt.
   buchen möchte. Es wird je nach Zahlungsbetrag und Zahlungsdatum ein sinnvoller
   Vorschlag gemacht.
 
-
 Kleinere neue Features und Detailverbesserungen:
 
+- Briefe werden auch im WebDAV archiviert. Ferner bessere Fehlerbehandlung und
+  E-Mail-Funktion aktiviert.
+
+- Mehrfachauswahl und Mengeneingabe für Artikel:
+  Wenn in den Belegmasken die Artikeleingabe nicht eindeutig ist, erscheint
+  eine Maske zur Artikelauswahl. Hierzu kann jetzt in den Benutzereinstellungen
+  eingestellt werden, dass in dieser Maske mehrere Artikel mit Mengen ausgewählt
+  werden können.
+
+- Stammdaten->Berichte->Waren: Nach Shopartikel filtern und anzeigen können.
+
+- Auftrags-/Angebotsbericht: Erfassungszeit als letzte Sortierung verwenden,
+  damit die Einträge nach Eingabezeitpunkt sortiert sind, wenn es
+  gleichrangige Einträge in der aktuellen Sortierung gibt.
+
+- Bei Eingabe nicht eindeutiger Artikel in den Belegmasken bleibt jetzt auch die
+  Mengeneingabe über die Auswahlmaske hinweg bestehen. Damit kann man die Menge
+  auch schon vorher eingeben: Nicht eindeutiger Artikel, TAB, TAB, Menge, ENTER
+
+- In den Berichten zu Aufträgen, VK-Lieferscheinen, Warenstammdaten, Kunden-/
+  Lieferntenstammdaten kann das Erfassungsdatum angezeigt und danach gefiltert
+  werden.
+
+- Filtern/Anzeigen von benutzerdefinierten Variablen bei Kunden-/Lieferanten in
+  den Berichten Angebot/Aufträge und Verkaufsrechnungen
+
+- Filtern nach Kunden-/Lieferantentyp bei Lieferschein-Berichten.
+
+- Preisgruppe bei Stammdaten->Berichte->Kunden anzeigen lassen können.
+
+
+Bugfixes:
+
+- Bugfix #54 Fehlermeldung im Mahnlauf bei automatischer Rechnung über Mahngebühren
+- Bugfix #50 Kundentyp-Rabatt wird falsch übernommen
+
+
 
 2015-04-10 - Release 3.2.1
 
index 5170e1d..71bb766 100644 (file)
@@ -3,6 +3,7 @@
  <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <meta http-equiv="refresh" content="0;URL=controller.pl?action=LoginScreen/user_login">
+  <link rel='shortcut icon' href='favicon.ico' type='image/x-icon'>
  </head>
  <body>
   <a href="controller.pl?action=LoginScreen/user_login">kivitendo-Login</a>
index a0e6eab..6d89819 100644 (file)
@@ -131,6 +131,7 @@ ns.eval_json_result = function(data) {
       else if (action[0] == 'reinit_widgets')       kivi.reinit_widgets();
       else if (action[0] == 'run')                  kivi.run(action[1], action.slice(2, action.length));
       else if (action[0] == 'run_once_for')         kivi.run_once_for(action[1], action[2], action[3]);
+      else if (action[0] == 'scroll_into_view')     $(action[1])[0].scrollIntoView();
 
       else                                          console.log('Unknown action: ' + action[0]);
 
index 97f3b5b..dfc0f97 100755 (executable)
@@ -178,7 +178,6 @@ $self->{texts} = {
   'Add User Group'              => 'Neue Benutzergruppe',
   'Add Vendor'                  => 'Lieferant erfassen',
   'Add Vendor Invoice'          => 'Einkaufsrechnung erfassen',
-  'Add Vendor Letter'           => '',
   'Add Warehouse'               => 'Lager erfassen',
   'Add and edit units'          => 'Einheiten erfassen und bearbeiten',
   'Add bank account'            => 'Bankkonto erfassen',
@@ -1067,6 +1066,7 @@ $self->{texts} = {
   'Edit requirement spec template' => 'Pflichtenheftvorlage bearbeiten',
   'Edit requirement spec type'  => 'Pflichtenhefttypen bearbeiten',
   'Edit risk level'             => 'Risikograd bearbeiten',
+  'Edit sales letters'          => 'Verkaufsbrief erstellen',
   'Edit sales price rule'       => 'Verkaufspreisregel bearbeiten',
   'Edit section #1'             => 'Abschnitt #1 bearbeiten',
   'Edit taxzone'                => 'Steuerzone bearbeiten',
@@ -1394,6 +1394,7 @@ $self->{texts} = {
   'Information'                 => 'Information',
   'Initial version.'            => 'Initiale Version.',
   'Insert'                      => 'Einfügen',
+  'Insert Date'                 => 'Erfassungsdatum',
   'Insert new'                  => 'Hinzufügen',
   'Insert with new customer/vendor number' => 'Mit neuer Kunden-/Lieferantennummer anlegen',
   'Insert with new database ID' => 'Neu anlegen mit neuer Datenbank-ID',
@@ -1454,6 +1455,7 @@ $self->{texts} = {
   'It will simply set the taxkey to 0 (meaning "no taxes") which is the correct value for such inventory transactions.' => 'Es wird einfach die Steuerschlüssel auf  0 setzen, was "keine Steuer" bedeutet und für solche Warenbestandsbuchungen der richtige Wert ist.',
   'Item deleted!'               => 'Artikel gelöscht!',
   'Item mode'                   => 'Artikelmodus',
+  'Item multi selection with qty' => 'Artikel-Mehrfachauswahl mit Menge',
   'Item not on file!'           => 'Dieser Artikel ist nicht in der Datenbank!',
   'Item values'                 => 'Artikelwerte',
   'Item variables'              => 'Artikelvariablen',
@@ -1863,13 +1865,13 @@ $self->{texts} = {
   'Paid'                        => 'bezahlt',
   'Paid amount'                 => 'Bezahlter Betrag',
   'Part'                        => 'Ware',
+  'Part "#1" has chargenumber or best before date set. So it cannot be transfered automaticaly.' => 'Bei Artikel "#1" ist eine Chargenummer oder ein Mindesthaltbarkeitsdatum vergeben. Deshalb kann dieser Artikel nicht automatisch ausgelagert werden.',
   'Part (database ID)'          => 'Artikel (Datenbank-ID)',
   'Part Description'            => 'Artikelbeschreibung',
   'Part Description missing!'   => 'Artikelbezeichnung fehlt!',
   'Part Notes'                  => 'Bemerkungen',
   'Part Number'                 => 'Artikelnummer',
   'Part Number missing!'        => 'Artikelnummer fehlt!',
-  'Part "#1" has chargenumber or best before date set. So it cannot be transfered automaticaly.' => 'Bei Artikel "#1" ist eine Chargenummer oder ein Mindesthaltbarkeitsdatum vergeben. Deshalb kann dieser Artikel nicht automatisch ausgelagert werden.',
   'Part picker'                 => 'Artikelauswahl',
   'Partial invoices'            => 'Teilrechnungen',
   'Partnumber'                  => 'Artikelnummer',
@@ -1931,7 +1933,6 @@ $self->{texts} = {
   'Picture #1: #2'              => 'Abbildung #1: #2',
   'Pictures for parts'          => 'Bilder für Waren',
   'Pictures for search parts'   => 'Bilder für Warensuche',
-  'Pirce rules must have at least one rule.' => 'Preisregeln brauchen mindestens eine Bedingung.',
   'Please Check the bank information for each customer:' => 'Bitte überprüfen Sie die Bankinformationen der Kunden:',
   'Please Check the bank information for each vendor:' => 'Bitte überprüfen Sie die Kontoinformationen der Lieferanten:',
   'Please ask your administrator to create warehouses and bins.' => 'Bitten Sie Ihren Administrator, dass er Lager und Lagerpl&auml;tze anlegt.',
@@ -1940,6 +1941,7 @@ $self->{texts} = {
   'Please contact your administrator or a service provider.' => 'Bitte kontaktieren Sie Ihren Administrator oder einen Dienstleister.',
   'Please contact your administrator.' => 'Bitte wenden Sie sich an Ihren Administrator.',
   'Please correct the settings and try again or deactivate that client.' => 'Bitte korrigieren Sie die Einstellungen und versuchen Sie es erneut, oder deaktivieren Sie diesen Mandanten.',
+  'Please create/copy a template named letter.tex in your client template dir' => 'Bitte erstellen / kopieren Sie eine Druckvorlage namens letter.tex in Ihren Mandantenvorlagen-Ordner',
   'Please define a taxkey for the following taxes and run the update again:' => 'Bitte definieren Sie einen Steuerschlüssel für die folgenden Steuern und starten Sie dann das Update erneut:',
   'Please do so in the administration area.' => 'Bitte erledigen Sie dies im Administrationsbereich.',
   'Please enter a profile name.' => 'Bitte geben Sie einen Profilnamen an.',
@@ -2007,6 +2009,7 @@ $self->{texts} = {
   'Price group (name)'          => 'Preisgruppe (Name) ',
   'Price information'           => 'Preisinformation',
   'Price or discount must not be zero.' => 'Preis/Rabatt darf nicht 0,00 sein',
+  'Price rules must have at least one rule.' => 'Preisregeln brauchen mindestens eine Bedingung.',
   'Price sources deactivated in this client' => 'Preisquellen die in diesem Mandanten deaktiviert sind',
   'Price type explanation'      => 'Preistyp Erklärung',
   'Pricegroup'                  => 'Preisgruppe',
@@ -2222,6 +2225,9 @@ $self->{texts} = {
   'Row was linked to another record' => 'Zeile wurde über einen anderen Beleg verlinkt',
   'Row was source for current record' => 'Zeile war Quelle für aktuellen Beleg',
   'Rule Details'                => 'Regel Details',
+  'Rule for customer must not be empty' => 'Eine Kundenbedingung darf nicht leer sein',
+  'Rule for part must not be empty' => 'Eine Warenbedingung darf nicht leer sein',
+  'Rule for vendor must not be empty' => 'Eine Lieferantenbedingung darf nicht leer sein',
   'Run JavaScript unit tests'   => 'JavaScript-Unit-Tests ausführen',
   'Run at'                      => 'Ausgeführt um',
   'Run tests'                   => 'Tests ausführen',
@@ -2412,6 +2418,7 @@ $self->{texts} = {
   'Show parts'                  => 'Artikel anzeigen',
   'Show requirement spec'       => 'Pflichtenheft anzeigen',
   'Show requirement spec template' => 'Pflichtenheftvorlage anzeigen',
+  'Show sales letters report'   => 'Verkaufsbrief anzeigen',
   'Show settings'               => 'Einstellungen anzeigen',
   'Show the picture in the part form' => 'Bild in Warenmaske anzeigen',
   'Show the pictures in the result for search parts' => 'Bilder in Suchergebnis für Stammdaten -> Berichte -> Waren anzeigen',
@@ -2696,7 +2703,6 @@ $self->{texts} = {
   'The following currencies have been used, but they are not defined:' => 'Die folgenden Währungen wurden benutzt, sind aber nicht ordnungsgemäß in der Datenbank eingetragen:',
   'The following drafts have been saved and can be loaded.' => 'Die folgenden Entw&uuml;rfe wurden gespeichert und k&ouml;nnen geladen werden.',
   'The following groups are valid for this client' => 'Die folgenden Gruppen sind für diesen Mandanten gültig',
-  'The following letter drafts have been saved and can be loaded.' => 'Die folgenden Briefentwürfe sind vorhanden und können geladen werden.',
   'The following list has been generated automatically from existing users collapsing users with identical settings into a single entry.' => 'Die folgende Liste wurde automatisch aus den im System vorhandenen Benutzern zusammengestellt, wobei identische Einstellungen zu einem Eintrag zusammengefasst wurden.',
   'The following old files whose settings have to be merged manually into the new configuration file "config/kivitendo.conf" still exist:' => 'Es existieren noch die folgenden alten Dateien, deren Einstellungen manuell in die neue Konfiguratsdatei "config/kivitendo.conf" migriert werden müssen:',
   'The following transaction contains wrong taxes:' => 'Die folgende Buchung enthält falsche Steuern:',
@@ -3092,7 +3098,6 @@ $self->{texts} = {
   'Vendor Discount'             => 'Lieferantenrabatt',
   'Vendor Invoice'              => 'Einkaufsrechnung',
   'Vendor Invoices & AP Transactions' => 'Einkaufsrechnungen & Kreditorenbuchungen',
-  'Vendor Letters'              => '',
   'Vendor Name'                 => 'Lieferantenname',
   'Vendor Number'               => 'Lieferantennummer',
   'Vendor Order Number'         => 'Bestellnummer beim Lieferanten',
@@ -3195,7 +3200,6 @@ $self->{texts} = {
   'You have to grant users access to one or more clients.' => 'Benutzern muss dann Zugriff auf einzelne Mandanten gewährt werden.',
   'You have to specify a department.' => 'Sie müssen eine Abteilung wählen.',
   'You have to specify an execution date for each antry.' => 'Sie müssen für jeden zu buchenden Eintrag ein Ausführungsdatum angeben.',
-  'You must choose a user.'     => '',
   'You must chose a user.'      => 'Sie m&uuml;ssen einen Benutzer ausw&auml;hlen.',
   'You must enter a name for your new print templates.' => 'Sie müssen einen Namen für die neuen Druckvorlagen angeben.',
   'You must select existing print templates or create a new set.' => 'Sie müssen vorhandene Druckvorlagen auswählen oder einen neuen Satz anlegen.',
@@ -3243,7 +3247,6 @@ $self->{texts} = {
   'bis'                         => 'bis',
   'building data'               => 'Verarbeite Daten',
   'building report'             => 'Erstelle Bericht',
-  'button'                      => '',
   'cash'                        => 'Ist-Versteuerung',
   'chargenumber #1'             => 'Chargennummer #1',
   'chart_of_accounts'           => 'kontenuebersicht',
index d542cab..0d34668 100644 (file)
@@ -22,7 +22,7 @@ order=< > \n
 \n=<br>
 
 [Template/LaTeX]
-order=\\ <pagebreak> & \n \r " $ <bullet> % _ # ^ { } < > £ ± ² ³ ° § ® © \xad \xa0 ➔ → ← |
+order=\\ <pagebreak> & \n \r " $ <bullet> % _ # ^ { } < > £ ± ² ³ ° § ® © \xad \xa0 ➔ → ← | −
 \\=\\textbackslash\s
 <pagebreak>=
 "=''
@@ -55,6 +55,7 @@ _=\\_
 ←=$\\leftarrow$
 \xa0=~
 |=\\textbar
+−={\\textemdash}
 
 [Template/OpenDocument]
 order=& < > " ' \x80 \n \r
index d3f5498..2bdde42 100644 (file)
@@ -1142,6 +1142,7 @@ $self->{texts} = {
   'Increase'                    => '',
   'Individual Items'            => '',
   'Information'                 => '',
+  'Insert Date'                 => '',
   'Insert with new customer/vendor number' => '',
   'Insert with new database ID' => '',
   'Insert with new part number' => '',
@@ -1192,6 +1193,7 @@ $self->{texts} = {
   'It will simply set the taxkey to 0 (meaning "no taxes") which is the correct value for such inventory transactions.' => '',
   'Item deleted!'               => '',
   'Item mode'                   => '',
+  'Item multi selection with qty' => '',
   'Item not on file!'           => '',
   'Item values'                 => '',
   'Item variables'              => '',
index 1b64573..43b290a 100755 (executable)
@@ -71,9 +71,7 @@ if (!defined $check{a}
 }
 
 if ($check{a}) {
-  foreach my $check (keys %check) {
-    $check{$check} = 1 unless defined $check{$check};
-  }
+  $check{$_} //= 1 for qw(o d l r);
 }
 
 
diff --git a/sql/Pg-upgrade2/ar_ap_fix_notes_as_html_for_non_invoices.pl b/sql/Pg-upgrade2/ar_ap_fix_notes_as_html_for_non_invoices.pl
new file mode 100644 (file)
index 0000000..4f40139
--- /dev/null
@@ -0,0 +1,52 @@
+# @tag: ar_ap_fix_notes_as_html_for_non_invoices
+# @description: Kreditoren-/Debitorenbuchungen: Bemerkungsfeld darf kein HTML sein
+# @depends: oe_ar_ap_delivery_orders_edit_notes_as_html
+package SL::DBUpgrade2::ar_ap_fix_notes_as_html_for_non_invoices;
+
+use strict;
+use utf8;
+
+use SL::DBUtils;
+
+use parent qw(SL::DBUpgrade2::Base);
+
+sub fix_column {
+  my ($self, $table) = @_;
+
+  my $sth = $self->dbh->prepare(qq|UPDATE $table SET notes = ? WHERE id = ?|) || $self->dberror;
+
+  my $query = <<SQL;
+    SELECT id, notes
+    FROM $table
+    WHERE (notes IS NOT NULL)
+      AND (NOT COALESCE(invoice, FALSE))
+      AND (itime < (
+        SELECT itime
+        FROM schema_info
+        WHERE tag = 'oe_ar_ap_delivery_orders_edit_notes_as_html'))
+SQL
+
+  foreach my $row (selectall_hashref_query($::form, $self->dbh, $query)) {
+    next if !$row->{notes} || (($row->{notes} !~ m{^<[a-z]+>}) && ($row->{notes} !~ m{</[a-z]+>$}));
+
+    my $new_content =  $row->{notes};
+    $new_content    =~ s{^<p>|</p>$}{}gi;
+    $new_content    =~ s{<br */>}{\n}gi;
+    $new_content    =~ s{</p><p>}{\n\n}gi;
+    $new_content    =  $::locale->unquote_special_chars('html', $new_content);
+
+    $sth->execute($new_content, $row->{id}) if $new_content ne $row->{notes};
+  }
+
+  $sth->finish;
+}
+
+sub run {
+  my ($self) = @_;
+
+  $self->fix_column($_) for qw(ar ap);
+
+  return 1;
+}
+
+1;
index b1e1a0b..3343c6f 100644 (file)
@@ -39,22 +39,27 @@ SQL
     }
     $sth->finish;
 
+    my $taxzone_charts_update_query = "INSERT INTO taxzone_charts (taxzone_id, buchungsgruppen_id, income_accno_id, expense_accno_id) VALUES (?, ?, ?, ?)";
+    $sth = $self->dbh->prepare($taxzone_charts_update_query);
+
     # convert Buchungsgruppen to taxzone_charts if any exist
     # the default swiss COA doesn't have any, for example
-    if ( scalar @{ $::form->{buchungsgruppen} } > 0 ) { 
-        my $taxzone_charts_update_query;
+    if ( scalar @{ $::form->{buchungsgruppen} } > 0 ) {
         foreach my $taxzone (  @{$::form->{taxzones}} ) {
             foreach my $buchungsgruppe (  @{$::form->{buchungsgruppen}} ) {
                 my $id = $taxzone->{id};
                 my $income_accno_id = $buchungsgruppe->{"income_accno_id_$id"};
                 my $expense_accno_id = $buchungsgruppe->{"expense_accno_id_$id"};
-                # TODO: check if the variables have a value
-                $taxzone_charts_update_query .= "INSERT INTO taxzone_charts (taxzone_id, buchungsgruppen_id, income_accno_id, expense_accno_id) VALUES ('$taxzone->{id}', '$buchungsgruppe->{id}', $income_accno_id, $expense_accno_id);\n";
+                my @values           = ($taxzone->{id}, $buchungsgruppe->{id}, $income_accno_id, $expense_accno_id);
+                $sth->execute(@values) && next;
+                $taxzone_charts_update_query =~ s{\?}{shift(@values)}eg;
+                $::form->dberror($taxzone_charts_update_query);
             };
         };
-        $self->db_query($taxzone_charts_update_query) if $taxzone_charts_update_query;
     };
 
+    $sth->finish;
+
     my $clean_buchungsgruppen_query = <<SQL;
 alter table buchungsgruppen drop column income_accno_id_0;
 alter table buchungsgruppen drop column income_accno_id_1;
diff --git a/sql/Pg-upgrade2/delete_invalidated_custom_variables_for_parts.sql b/sql/Pg-upgrade2/delete_invalidated_custom_variables_for_parts.sql
new file mode 100644 (file)
index 0000000..a07d326
--- /dev/null
@@ -0,0 +1,13 @@
+-- @tag: delete_invalidated_custom_variables_for_parts
+-- @description: Bei Artikeln ungültig gesetzte, benutzerdefinierte Variablen löschen
+-- @depends: release_3_2_0
+DELETE FROM custom_variables
+WHERE (config_id IN (
+    SELECT id
+    FROM custom_variable_configs
+    WHERE module = 'IC'))
+  AND EXISTS (
+    SELECT val.id
+    FROM custom_variables_validity val
+    WHERE (val.config_id = custom_variables.config_id)
+      AND (val.trans_id  = custom_variables.trans_id));
index db80238..7965730 100644 (file)
@@ -1,7 +1,5 @@
 -- @tag: remove_obsolete_trigger
 -- @description: Entfernt veraltete Trigger check_inventory
--- @depends: release_3_2_0
--- @encoding: utf-8
 
 -- drop triggers
 DROP TRIGGER IF EXISTS check_inventory           ON oe;
index 00a654e..5c22135 100644 (file)
@@ -1,6 +1,6 @@
 -- @tag: taxzone_id_in_oe_delivery_orders
 -- @description: Werte für Inland in Spalte taxzone_id in Tabellen oe und delivery_orders in Foreign Key zu tax_zones konvertieren; NULL-Werte in ap/ar verhindern; Spalten NOT NULL setzen
--- @depends: change_taxzone_id_0
+-- @depends: change_taxzone_id_0 remove_obsolete_trigger
 
 UPDATE oe              SET taxzone_id = (SELECT id FROM tax_zones WHERE description = 'Inland') WHERE (taxzone_id = 0) OR (taxzone_id IS NULL);
 UPDATE delivery_orders SET taxzone_id = (SELECT id FROM tax_zones WHERE description = 'Inland') WHERE (taxzone_id = 0) OR (taxzone_id IS NULL);
index 4727bd8..e53fcec 100644 (file)
@@ -1,6 +1,6 @@
 -- @tag: warehouse_add_delivery_order_items_stock_id
 -- @description: Constraints für inventory auf delivery_order (dois und do). Ferner sinnvolle Umbenennung zumindestens von einer Spalte (orderitems -> dois). <br><b>Falls die Constraint nicht gesetzt werden kann, kontaktieren Sie einen Dienstleister und / oder löschen sie die Verknüpfung der Warenbewegung mit Lieferschein auf eigene Verantwortung mit: "UPDATE inventory SET oe_id = NULL WHERE oe_id NOT IN (select id from delivery_orders);"<br>Hintergrund: Eingelagerte Lieferscheine können / sollen nicht gelöscht werden, allerdings weist dieser Datenbestand genau diesen Fall auf.</b>
--- @depends: release_3_1_0
+-- @depends: release_3_1_0 remove_obsolete_trigger
 ALTER TABLE inventory RENAME orderitems_id TO delivery_order_items_stock_id;
 ALTER TABLE inventory ADD CONSTRAINT delivery_order_items_stock_id_fkey FOREIGN KEY (delivery_order_items_stock_id) REFERENCES delivery_order_items_stock (id);
 ALTER TABLE inventory ADD CONSTRAINT oe_id_fkey FOREIGN KEY (oe_id) REFERENCES delivery_orders (id);
index ae42e8f..e741cdd 100644 (file)
@@ -47,6 +47,7 @@ puhs
 sekf
 sucess
 varsion
+pirce
 );
 
 $testcount = scalar(@Support::Files::testitems);
index 13b060a..ac7875b 100644 (file)
         </td>
       </tr>
 
+      <tr>
+        <th align="right">[% 'Item multi selection with qty' | $T8 %]</th>
+        <td>
+          [% L.yes_no_tag('item_multiselect', myconfig_item_multiselect) %]
+        </td>
+      </tr>
+
     </table>
    </div>
 
index 0790e2c..b43630f 100644 (file)
       [% L.date_tag('transdateto') %]
      </td>
     </tr>
+
+[%- IF CT_CUSTOM_VARIABLES.size %]
+    <tr>
+      <td></td>
+      <td colspan=4 align=left><b>[% 'Custom variables for module' | $T8 %]: [%'Customers and vendors' | $T8 %]</td>
+    </tr>
+    [% CT_CUSTOM_VARIABLES_FILTER_CODE %]
+[%- END %]
+
    <input type=hidden name=sort value=transdate>
    </table>
     </td>
            <td align=right><input name="l_ustid" id="l_ustid" class=checkbox type=checkbox value=Y></td>
            <td nowrap>[% 'USt-IdNr.' | $T8 %]</td>
           </tr>
+
+          <table>
+            <tr><td align="right">
+            [% CT_CUSTOM_VARIABLES_INCLUSION_CODE %]
+            </td></tr>
+          </table>
+
          </table>
         </td>
        </tr>
index 933a7d1..aa51707 100644 (file)
    </tr>
    [% END %]
 
+   <tr>
+    <th align="right">[% 'Insert Date' | $T8 %] [% 'From' | $T8 %]</th>
+    <td>
+      [% L.date_tag('insertdatefrom') %]
+    </td>
+    <th align="right">[% 'Bis' | $T8 %]</th>
+    <td>
+      [% L.date_tag('insertdateto') %]
+    </td>
+   </tr>
+
    [% CUSTOM_VARIABLES_FILTER_CODE %]
 
    <tr>
@@ -86,7 +97,7 @@
 
    <tr>
     <th align="right" nowrap>[% 'Include in Report' | $T8 %]</th>
-    <td>
+    <td colspan="5">
      <table border="0">
       <tr>
        <td>
         <input name="l_payment" id="l_payment" type="checkbox" class="checkbox" value="Y">
         <label for="l_payment">[% 'Payment Terms' | $T8 %]</label>
        </td>
+       <td>
+        <input name="l_insertdate" id="l_insertdate" class="checkbox" type="checkbox" value="Y">
+        <label for="l_insertdate">[% 'Insert Date' | $T8 %]</label>
+       </td>
       [% IF IS_CUSTOMER %]
       <td>
        <input name="l_salesman" id="l_salesman" type="checkbox" class="checkbox" value="Y">
        <label for="l_salesman">[% 'Salesman' | $T8 %]</label>
       </td>
+      </tr>
+      <tr>
+      <td>
+       <input name="l_pricegroup" id="l_pricegroup" type="checkbox" class="checkbox" value="Y">
+       <label for="l_pricegroup">[% 'Pricegroup' | $T8 %]</label>
+      </td>
       [% END %]
       </tr>
 
index 9b4e9f2..2a28853 100644 (file)
         </td>
        </tr>
 
+       [%- IF is_customer %]
+       <tr>
+        <th align="right" nowrap>[% 'Insert Date' | $T8 %]</th>
+        <td>[% insertdate %]</td>
+       </tr>
+       [%- END %]
+
        <tr>
         <th width="70%" align="right" nowrap>[% 'Project Number' | $T8 %]</th>
         <td>
index e84bbf3..baa00bb 100644 (file)
@@ -9,6 +9,8 @@
  [%- SET is_customer = '0' %]
  [%- END %]
 
+ [%- SET vctypelabel = vc == 'customer' ? LxERP.t8('Customer type') : LxERP.t8('Vendor type') %]
+
  <script type="text/javascript">
    $(function(){ document.Form.donumber.focus(); });
  </script>
      <td colspan="3"><input name="serialnumber" class="fixed_width"></td>
     </tr>
 
+    [%- IF ALL_BUSINESS_TYPES.size %]
+    <tr>
+     <th align="right" nowrap>[% vctypelabel %]</th>
+     <td colspan="3">
+      [% L.select_tag('business_id', ALL_BUSINESS_TYPES, title_key = 'description', with_empty = 1, style='width:250px') %]
+     </td>
+    </tr>
+    [%- END %]
+
     <tr>
      <th align="right">[% 'Delivery Order Date' | $T8 %] [% 'From' | $T8 %]</th>
      <td>
      </td>
     </tr>
 
+    [%- IF is_customer %]
+    <tr>
+     <th align="right">[% 'Insert Date' | $T8 %] [% 'From' | $T8 %]</th>
+     <td>
+       [% L.date_tag('insertdatefrom') %]
+     </td>
+     <th align="right">[% 'Bis' | $T8 %]</th>
+     <td>
+       [% L.date_tag('insertdateto') %]
+     </td>
+    </tr>
+    [%- END %]
+
     <tr>
      <th align="right">[% 'Include in Report' | $T8 %]</th>
      <td colspan="5">
          <input name="l_reqdate" id="l_reqdate" class="checkbox" type="checkbox" value="Y" checked>
          <label for="l_reqdate">[% 'Reqdate' | $T8 %]</label>
         </td>
+        [% IF is_customer %]
+        <td>
+         <input name="l_insertdate" id="l_insertdate" class="checkbox" type="checkbox" value="Y">
+         <label for="l_insertdate">[% 'Insert Date' | $T8 %]</label>
+        </td>
+        [%- END %]
        </tr>
 
        <tr>
index 4433c82..8e71cb6 100644 (file)
        <td><input name="microfiche" size="20"></td>
       </tr>
 
+      <tr>
+       <th align="right" nowrap>[% 'Shopartikel' | $T8 %]</th>
+       <td>[% L.yes_no_tag('shop', shop, default='', with_empty=1, empty_title='---') %]</td>
+      </tr>
+
+      <tr>
+       <th align="right">[% 'Insert Date' | $T8 %]</th>
+       <td>
+        [% 'From' | $T8 %][% L.date_tag('insertdatefrom') %]
+        [% 'Bis' | $T8 %] [% L.date_tag('insertdateto') %]
+       </td>
+      </tr>
+
       [% CUSTOM_VARIABLES_FILTER_CODE %]
 
       [%- IF is_assembly %]
          <tr>
           <td>[%- L.checkbox_tag('l_notes', label=LxERP.t8('Notes'), value='Y') %]</td>
           <td>[%- L.checkbox_tag('l_name', label=LxERP.t8('Name in Selected Records'), value='Y') %]</td>
+          <td>[%- L.checkbox_tag('l_shop', label=LxERP.t8('Shopartikel'), value='Y') %]</td>
+          <td>[%- L.checkbox_tag('l_insertdate', label=LxERP.t8('Insert Date'), value='Y') %]</td>
          </tr>
 
          [% CUSTOM_VARIABLES_INCLUSION_CODE %]
index 9252b45..11dfbf6 100644 (file)
@@ -5,7 +5,11 @@
 
   <table width="100%">
    <tr class="listheading">
-    <th>&nbsp;</th>
+    [%- IF myconfig_item_multiselect %]
+      <th>[% LxERP.t8('Qty') %]</th>
+    [%- ELSE %]
+      <th>&nbsp;</th>
+    [%- END %]
     <th>[% LxERP.t8('Number') %]</th>
     <th>[% LxERP.t8('Part Description') %]</th>
     <th>[% LxERP.t8('Other Matches') %]</th>
 
    [%- FOREACH item = ITEM_LIST %]
    <tr class="listrow[% loop.count % 2 %]">
-    <td><input name="select_item_id" class="radio" type="radio" value="[% HTML.escape(item.id) %]"[% IF loop.first %] checked[% END %]></td>
+    [%- IF myconfig_item_multiselect %]
+      <td>[% L.input_tag('select_qty_' _ HTML.escape(item.id), '', size => 5) %]</td>
+    [%- ELSE %]
+      <td><input name="select_item_id" class="radio" type="radio" value="[% HTML.escape(item.id) %]"[% IF loop.first %] checked[% END %]></td>
+    [%- END %]
     <td>[% HTML.escape(item.partnumber) %]</td>
     <td>[% HTML.escape(item.description) %]</td>
     <td>[% HTML.escape(item.matches).join('<br>') %]</td>
 
   [% L.submit_tag('action', LxERP.t8('Continue')) %]
  </form>
+
+[%- IF myconfig_item_multiselect %]
+ <script type='text/javascript'>
+   var first_click = 1;;
+   [%- FOREACH item = ITEM_LIST %]
+     [% SET THIS_ID = 'select_qty_' _ HTML.escape(item.id) %]
+     $('#[% THIS_ID %]').click(function(){
+       var qty = '1';
+       if (first_click) {
+         qty = '[% LxERP.format_amount(PRE_ENTERED_QTY, 5) %]';
+       }
+       first_click = 0;
+       if ($('#[% THIS_ID %]').attr('value') == '') {
+         $('#[% THIS_ID %]').attr('value', qty); $('#[% THIS_ID %]').select();
+       }
+     });
+   [%- END %]
+ </script>
+[%- END %]
index 5a8cb02..362bd89 100644 (file)
             <span class="plus[% IF is_credit_remaining_negativ %]0[% ELSE %]1[% END %]">[% LxERP.format_amount(creditremaining,0 ,'0') %]</span>
           </td>
         </tr>
+[%- IF business %]
+        <tr>
+          <th align="right">[% 'Vendor type' | $T8 %]</th>
+          <td>[% business %]; [% 'Trade Discount' | $T8 %] [% LxERP.format_amount(tradediscount * 100) %] %</td>
+        </tr>
+[%- END %]
         <tr>
           <th align="right" nowrap>[% 'Record in' | $T8 %]</th>
           <td colspan="3"><select name="AP" style="width:250px;">[% selectAP %]</select></td>
index 39aa737..bf91648 100644 (file)
 
 <input type="hidden" name="action" value="dispatcher">
 <input class="submit" type="submit" name="action_update" id="update_button" value="[% 'Update' | $T8 %]">
-<input class="submit" type="submit" name="action_print" value="[% 'Print' | $T8 %]">
-<input class="submit" type="submit" name="action_e_mail" value="[% 'E-mail' | $T8 %]">
+
+[%- IF letter.letternumber %]
+  <input class="submit" type="submit" name="action_print" value="[% 'Print' | $T8 %]">
+  <input class="submit" type="submit" name="action_e_mail" value="[% 'E-mail' | $T8 %]">
+[% END %]
+
 <input class="submit" type="submit" name="action_save" value="[% 'Save' | $T8 %]">
 <input class="submit" type="submit" name="action_save_letter_draft" value="[% 'Save Draft' | $T8 %]">
 
index 81ed919..773c089 100644 (file)
                       [% L.date_tag('reqdate', reqdate, id='reqdate') %]
                     </td>
                   </tr>
+                  [%- IF is_sales_ord %]
+                  <tr>
+                    <th align="right" nowrap>[% 'Insert Date' | $T8 %]</th>
+                    <td>[% insertdate %]</td>
+                  </tr>
+                  [%- END %]
                   <tr>
                     <th width="70%" align="right" nowrap>[% 'Project Number' | $T8 %]</th>
                     <td>
index 06f1429..323fd23 100644 (file)
        [% L.date_tag('reqdateto') %]
      </td>
     </tr>
+
+    [%- IF type == 'sales_order' %]
+    <tr>
+     <th align="right">[% 'Insert Date' | $T8 %] [% 'From' | $T8 %]</th>
+     <td>
+       [% L.date_tag('insertdatefrom') %]
+     </td>
+     <th align="right">[% 'Bis' | $T8 %]</th>
+     <td>
+       [% L.date_tag('insertdateto') %]
+     </td>
+    </tr>
+    [%- END %]
+
 [%- IF type == 'sales_quotation' %]
     <tr>
      <th align="right">[% 'Expected billing date' | $T8 %] [% 'From' | $T8 %]</th>
      </td>
     </tr>
 [%- END %]
+
+[%- IF CT_CUSTOM_VARIABLES.size %]
+    <tr>
+      <td></td>
+      <td colspan=4 align=left><b>[% 'Custom variables for module' | $T8 %]: [%'Customers and vendors' | $T8 %]</td>
+    </tr>
+    [% CT_CUSTOM_VARIABLES_FILTER_CODE %]
+[%- END %]
+
     <tr>
      <th align="right">[% 'Include in Report' | $T8 %]</th>
      <td colspan="5">
          <input name="l_reqdate" id="l_reqdate" class="checkbox" type="checkbox" value="Y" checked>
          <label for="l_reqdate">[% IF is_order %][% 'Required by' | $T8 %][% ELSE %][% 'Valid until' | $T8 %][% END %]</label>
         </td>
+        [%- IF type == 'sales_order' %]
+        <td>
+          <input name="l_insertdate" id="l_insertdate" class="checkbox" type="checkbox" value="Y">
+          <label for="l_insertdate">[% 'Insert Date' | $T8 %]</label>
+        </td>
+        [%- END %]
        </tr>
        <tr>
         <td>
          <label for="l_ustid">[% 'USt-IdNr.' | $T8 %]</label>
         </td>
        </tr>
+
+      [% CT_CUSTOM_VARIABLES_INCLUSION_CODE %]
+
 [%- IF type == 'sales_order' %]
        <tr><td colspan="3"><hr></td></tr>
 [%- END %]