]> wagnertech.de Git - mfinanz.git/commitdiff
Merge branch 'bankerweiterung_und_skonto'
authorG. Richardson <information@kivitendo-premium.de>
Thu, 7 May 2015 08:36:14 +0000 (10:36 +0200)
committerG. Richardson <information@kivitendo-premium.de>
Thu, 7 May 2015 08:36:14 +0000 (10:36 +0200)
Conflicts:
locale/de/all

77 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/Helper/GetModels.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.pm
SL/Dispatcher/AuthHandler/Admin.pm
SL/IC.pm
SL/IR.pm
SL/Locale/String.pm
SL/OE.pm
SL/PriceSource.pm
SL/PriceSource/Business.pm
SL/PriceSource/Discount.pm
SL/PriceSource/Pricegroup.pm
SL/PriceSource/Vendor.pm
SL/Webdav/VersionScheme/Timestamp.pm
bin/mozilla/ar.pl
bin/mozilla/ct.pl
bin/mozilla/dn.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
modules/fallback/Regexp/IPv6.pm [new file with mode: 0644]
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 7236196a9fbe66b54690034eb7e753c0b98a65b6..81be410e819f7d490d57c233be811c26a37aa2e0 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 e202b98e51e26fb0e63141af6951b8fb85e1bc3f..f6d9e96d79c4e6741ebd4624a81f5cd3d09f82b2 100644 (file)
@@ -7,6 +7,7 @@ use IO::File;
 use Time::HiRes qw(gettimeofday);
 use List::MoreUtils qw(uniq);
 use YAML;
+use Regexp::IPv6 qw($IPv6_re);
 
 use SL::Auth::ColumnInformation;
 use SL::Auth::Constants qw(:all);
@@ -541,7 +542,7 @@ sub restore_session {
   my $api_token_cookie = $self->get_api_token_cookie;
   my $cookie_is_bad    = !$cookie || $cookie->{is_expired};
   $cookie_is_bad     ||= $api_token_cookie && ($api_token_cookie ne $cookie->{api_token}) if  $api_token_cookie;
-  $cookie_is_bad     ||= $cookie->{ip_address} ne $ENV{REMOTE_ADDR}                       if !$api_token_cookie;
+  $cookie_is_bad     ||= $cookie->{ip_address} ne $ENV{REMOTE_ADDR}                       if !$api_token_cookie && $ENV{REMOTE_ADDR} !~ /^$IPv6_re$/;
   if ($cookie_is_bad) {
     $self->destroy_session();
     return $self->session_restore_result($cookie ? SESSION_EXPIRED() : SESSION_NONE());
@@ -930,20 +931,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 44adb406c1ea27770626bfddefafccc11f4feb78..cdf274b712dce558b15523c9e37ff2eeb8d9a0e8 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 2eedbee078c4b69657f825422dbb467754322fcc..d4775b83083160f97054e8cd324be8992367f7cd 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 6f30071b1a100b54237e52327be24a9904e9cb0b..ee3d3634363cbe2d43c952a4ac996fd0fcdfc0fd 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 47ccc241355c25a6df4de0d1e54c1ef4dd93f89d..fc0a16a1ff67574a7f8219d2741059695036e684 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 33d4d41cf48705c563500b6defac83667a66ae7f..bcaeb62c13f48795be25720f0f0df1a812e24cfe 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 9d406460d4d0aef26af4ce351168f930a1a71cd1..861834b83c9091748423c69e63fe5fa0d605d0e2 100644 (file)
@@ -157,10 +157,16 @@ sub get_models_url_params {
   $self->register_handlers('callback' => $callback);
 }
 
-sub get_callback {
+sub get_callback_params {
   my ($self, %override_params) = @_;
 
   my %default_params = $self->_run_handlers('callback', action => $self->controller->action_name);
+}
+
+sub get_callback {
+  my ($self, %override_params) = @_;
+
+  my %default_params = $self->get_callback_params(%override_params);
 
   return $self->controller->url_for(%default_params, %override_params);
 }
index 8e5a445deec49154914cf96747440627d7cfba7f..5bc780bf92732109a6aaf3dafdbd904abb5d3908 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 2c2498b048e09305af7371a39cd75037ba69bcdb..604978591f6a0f3a80c0a5dfb8ed26c7973d04f4 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 1eac6f937c3cee4d96eb1fe5aa1bb687729942a3..a8d8ab5139aa316186b6aab4100f17ca40722b24 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 10e319ba396de6ef8cd7a38c3b28e8f41e5e3250..3e6c03da58790873838ffee50debbdb6ef82c221 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 b530db73191b83d4fd5076043ee190be8475954b..436c9b1a5fc04e754c602ed1c7e647350d40769f 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 878300693214bfd6a00e26fe286b67ffed5e88c2..fb9e833cae77f8e024dfc2cfd8fc594a055a7bfd 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 c29cc0a446a351d90e371660f0856c2fc4100ad2..31ae7b6997fa848c4450f9e306ae5ba5536b387c 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 3ed6cd34e2d1845e8c0c03fbabd9cb0a154cef5d..065d2b3cd27f315425e3f990a6fc965b8925b06b 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 e566089f538f48573da03b513ad922b9c0150834..5671a4bb0e3a3ae7b6fed9be5d948e4886896483 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 48e2be6648544996e11819a57a2f3a0f41bf7d28..f73e32ca04273951909779b1eab621de0be54979 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 e33b3a3e4e7a8a613d1737d2bb1981fa91a9aa8c..70b5ab4da9f2d6b0cd203d8e07b3d33292fc5b2b 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 3fc39dfb8ce541224f3cb2918c02cfc1505a349c..8aa71dab3a1851a891f8077c361107d50e74c0bb 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 ed0523bb42afaf9870714325c0ecdaf6cc03e52a..ab1bd49576c455e1b1eeca07cf0d9d0b545de4f4 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 c9289e46fb10442e2a6b020ffa8a08c58a3d2d4b..55200835efd12d218ddb0c51cb10be1057f6bfad 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 80b1ae352f2bbb9306c54385dbc858954757e546..e764ac6c3351639e50ca58bc769502b522d1bfba 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 43d6fa75748bc028421e12a1b620d085c628e514..17aaab0c699cb22b6891a8385a34f61f2fed69ea 100644 (file)
@@ -25,7 +25,6 @@ use English qw(-no_match_vars);
 use File::Basename;
 use List::MoreUtils qw(all);
 use List::Util qw(first);
-use POSIX;
 use SL::ArchiveZipFixes;
 use SL::Auth;
 use SL::Dispatcher::AuthHandler;
index 9ef6ef388995f88b95361c05039983d4f9940232..06fd3cfe3c2991b42f831ddadf2788aef57ec1c6 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 27af46a50085d766bc0600f91f41982200fda192..e25e13e266ef6cec5bf66dd9f8e118a3f46ab4d7 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 208d2e4fb3cbb9e9800d9ce53553dcde312e19d0..8bd2f5f535626f6136ffdf37dcb12fb27df5664a 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 e05ae6abedc3841e14a2bc3aafa059e9a8d738d1..63a53231ba5d5c4aab7c9cdae067caf5f6ef339d 100644 (file)
@@ -11,7 +11,9 @@ use Rose::Object::MakeMethods::Generic (
 
 our @EXPORT = qw(t8);
 
-use overload '""' => \&translated;
+use overload
+  '""' => \&translated,
+  eq   => \&my_eq;
 
 sub translated {
   my ($self) = @_;
@@ -25,6 +27,10 @@ sub t8 {
   return SL::Locale::String->new(untranslated => $string, args => [ @_ ]);
 }
 
+sub my_eq {
+  $_[1] eq $_[0]->translated;
+}
+
 sub TO_JSON {
   return $_[0]->translated;
 }
index 8f7c050d7a6abdcea85447b4104a4154720ded49..b8dd56072cdc48807cb10995ad5aab560baf1d90 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 89dffba89c8de659730e1aad256ec17eaa9c3129..c8cdc6cd197a2201da5ee9ac6b56535aae91a272 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 ca66c5dba6096a2ffc6f9a685dcc5ca6d05edaac..fc7b298fc1979b1fc81969286daafdc3991270da 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 a7475eae2d84e610e877d5feecda191d4e45a5e0..ae4592bd97666618541fe8b9a52955f49e855ec4 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 8a322c5acb62a4fc20b7b6d387bf2d2cf60a577a..2a8adae13395a5beae6668ea4994f16ffae974e3 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 94d9bd01f689c00f17c65d03022fbee097d31500..0b09ad698f3aa0b2876cfe110de1e324cf58c370 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 9919b9e293e0600558cbf77352f775dbc6335adc..f13e1fbb8fe64a9b66a6146bf0eb97c36fcc9a95 100644 (file)
@@ -3,7 +3,7 @@ package SL::Webdav::VersionScheme::Timestamp;
 use strict;
 use parent qw(Rose::Object);
 
-use POSIX;
+use POSIX ();
 
 sub separator { "_" }
 
index 2960001fc39f3b39ce86dd72b282ad97c6cb48a2..6a024a3cffaf8aaa9a6baba84d62b9c5ac723c39 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 5a2dbd6c50bc2b9e054b0ba853a0aa3fcbb61d3e..ec1ccbfbaeab7cad7be3b985b3379eba84c40b5f 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 52ed58b18dc67143ce90f68d109feabe99c270b9..6b2cad0e66848a91aae829ff28866750c7615319 100644 (file)
@@ -31,7 +31,7 @@
 #
 #======================================================================
 
-use POSIX;
+use POSIX qw(strftime);
 
 use SL::IS;
 use SL::PE;
index 3f624181fc4d79ec0c1574182ce98284879891f9..33ab98c016dfef0b4abf671e430f7c8b7ce5657a 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 76f086938edb904379adc9463e3d4df07b4af466..f83d42a53b948127361e189caa5d076a5aeb6b3e 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 e5845758370846bb1bae5f6e80f486c280a16b2e..12bc4ae6ce44f13a2899fbc3c2dd9436a9ca21e0 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);
   }
@@ -1793,8 +1827,7 @@ sub _update_part_information {
 
   my $form     = $main::form;
 
-  my %part_information = IC->get_basic_part_info('id'        => [ grep { $_ } map { $form->{"id_${_}"} } (1..$form->{rowcount}) ],
-                                                 'vendor_id' => $form->{vendor_id});
+  my %part_information = IC->get_basic_part_info('id' => [ grep { $_ } map { $form->{"id_${_}"} } (1..$form->{rowcount}) ]);
 
   $form->{PART_INFORMATION} = \%part_information;
 
index 4cd96ca9f9322cd318315884980ba7ad8e599a16..5bd3548167d74d1ab34fa7a798f5c4843132866c 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 cac93daad9dde36106dc042e4908f649d33901e4..a4f9e85b9d7415cb52c85ea316121eb85ffdc0cc 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 18564d3c8144203d3225b76097bee32e80ad0604..d9a07423a16ee5b919747bb839b20a2f938b78cc 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 147e9d9de8a5a05bd7a2bfbec4503d3fb6eb4e72..93e9f2a2f8e1072aa2d92249477746b26bf6406b 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 fb8ec75a3022617c91c2ce58d2a42632e8e1955b..2594ef665ff0fc84932c44f2029448ba207bee44 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 5c9aaab4632b9a5ca5ee6de07cf1082d24970785..8df983b291740ffd35721a5c0d57c535a3599b7f 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 5170e1d4a0776baf16bf35374b9d05d3c46d4aa0..71bb76600cc2f2cda3a902b3b17e71b4f5d781c4 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 a0e6eab61ca541cf725e340b108d1cbd4b3576dc..6d898199088da375094c449c4e680ddc48867c95 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 6479bf15a74ff99dce0fbbc69da35a2763a2ae6b..6ccb262156205d05356c22e66bdd2a73f15cbca9 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',
@@ -1066,6 +1065,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 create a CSV import profile called "MT940" for the import type bank transactions:' => 'Bitte erstellen Sie ein CSV Import Profil mit dem Namen "MT940" für den Importtyp Bankbewegungen',
   '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.',
@@ -2008,6 +2010,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',
@@ -2223,6 +2226,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',
@@ -2413,6 +2419,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',
@@ -2698,7 +2705,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:',
@@ -3094,7 +3100,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',
@@ -3197,7 +3202,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.',
@@ -3245,7 +3249,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 d542caba07262ed15cac2b0c4f49d595615cd554..0d34668d4f816d5dbfec6c86c12b135ecba3378b 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 d3f54980ffcbd4fd19e6c2cdb1e405b45304ca49..2bdde42d93682a7803485d6b9a3d31fe6e5e1a97 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'              => '',
diff --git a/modules/fallback/Regexp/IPv6.pm b/modules/fallback/Regexp/IPv6.pm
new file mode 100644 (file)
index 0000000..24ecf5d
--- /dev/null
@@ -0,0 +1,65 @@
+package Regexp::IPv6;
+
+our $VERSION = '0.03';
+
+use strict;
+use warnings;
+
+require Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw($IPv6_re);
+
+my $IPv4 = "((25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))";
+my $G = "[0-9a-fA-F]{1,4}";
+
+my @tail = ( ":",
+       "(:($G)?|$IPv4)",
+             ":($IPv4|$G(:$G)?|)",
+             "(:$IPv4|:$G(:$IPv4|(:$G){0,2})|:)",
+       "((:$G){0,2}(:$IPv4|(:$G){1,2})|:)",
+       "((:$G){0,3}(:$IPv4|(:$G){1,2})|:)",
+       "((:$G){0,4}(:$IPv4|(:$G){1,2})|:)" );
+
+our $IPv6_re = $G;
+$IPv6_re = "$G:($IPv6_re|$_)" for @tail;
+$IPv6_re = qq/:(:$G){0,5}((:$G){1,2}|:$IPv4)|$IPv6_re/;
+$IPv6_re =~ s/\(/(?:/g;
+$IPv6_re = qr/$IPv6_re/;
+
+1;
+__END__
+
+=head1 NAME
+
+Regexp::IPv6 - Regular expression for IPv6 addresses
+
+=head1 SYNOPSIS
+
+  use Regexp::IPv6 qw($IPv6_re);
+
+  $address =~ /^$IPv6_re$/ and print "IPv6 address\n";
+
+=head1 DESCRIPTION
+
+This module exports the $IPv6_re regular expression that matches any
+valid IPv6 address as described in "RFC 2373 - 2.2 Text Representation
+of Addresses" but C<::>. Any string not compliant with such RFC will
+be rejected.
+
+To match full strings use C</^$IPv6_re$/>.
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2009, 2010 by Salvador FandiE<ntilde>o
+(sfandino@yahoo.com)
+
+This library is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself, either Perl version 5.10.0 or,
+at your option, any later version of Perl 5 you may have available.
+
+Additionally, you are allowed to use the regexp generated by the
+module in any way you want, without any restriction. For instance, you
+are allowed to copy it verbating in your program.
+
+=cut
+
index 1b645733c774780ac8a8a4163f139359c8c0a1ba..43b290a35322b9e56f35304b8046496270cb77b0 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 b1e1a0bb60eedde540db2215ee3f24c76f505dcf..3343c6f1239f6b74ca21313464b9809f8a356e9f 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 db802386dca4ac056008d31125d60d38fd3b25a0..796573065d919815098e10ac7c7c1d50f69b2d0a 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 00a654e5ab3404c427392f8fc323c923169042ab..5c22135becf8663715f76fc76efcdfe9b97931f1 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 4727bd81b1484402b800121740a9b731359f4d66..e53fcecce644865c6ff8da94eb79fd89c5ff7917 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 ae42e8f7f20b1160baaf204773b84c4667dabf56..e741cdd626669f3890c8701363e653fcf1d9fde8 100644 (file)
@@ -47,6 +47,7 @@ puhs
 sekf
 sucess
 varsion
+pirce
 );
 
 $testcount = scalar(@Support::Files::testitems);
index 13b060a161cfd103b9b9d2d52d2a31bca60bcf28..ac7875b11e627f8b9fd6552ef0c10b2b6782da76 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 0790e2c85cafd96a333a0f4dfa6586a285c8b032..b43630fad19fd76f0d03c673a7a8fb66d601f7a6 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 933a7d1c063dd39625bece9c993a9bddbb6171fe..aa517079faaaa647bfbe9306088ea53272d23ffa 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 9b4e9f26f0a5a9223b09af5ee96aeef8d930b7c1..2a28853d5ebd6841efad0f43d1d5be71ce59d061 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 e84bbf3e4dbf934d6a64b6718945fbbdf4e4193c..baa00bb189cf45f42c6c09786ea37020e8804131 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 4433c82a1398955701875224ae837e49078accf3..8e71cb65952eef9f07ee024ddb53132ec28a67f4 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 9252b45fc3899a80ce17ed5881d9d7567343cbd8..11dfbf609c8747551a1b83ca60418ebf5cfd20b5 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 5a8cb024ad7bef48b0c92eb9b0efdea220151780..362bd89576a85b0d10d8690f29192e75b41bd636 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 39aa7373fa3b0fb6b6e48f2e4b420c8ee5f70a77..bf91648d6cc97ff0528b7603869a3498d1e38081 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 81ed919aff033c399b8566f568de88f54e541828..773c0894ab7ec0806bab546de8f80506357f7178 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 06f1429c77e23335249c8112d5afb3190f8e36f3..323fd2383fd4b647537b44d69b9a990dcaf87f09 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 %]