From: G. Richardson Date: Tue, 5 May 2015 07:56:42 +0000 (+0200) Subject: Merge branch 'bankerweiterung_und_skonto' X-Git-Tag: release-3.3.0beta~106 X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=a87694dc525b4eabff11029f1e5401621e2bd3bc;hp=fc35d1dd4441b0dd26fe0b1b397cc66508a715f3;p=kivitendo-erp.git Merge branch 'bankerweiterung_und_skonto' Conflicts: doc/changelog locale/de/all --- diff --git a/SL/AR.pm b/SL/AR.pm index 7236196a9..81be410e8 100644 --- 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'; diff --git a/SL/Auth.pm b/SL/Auth.pm index e202b98e5..5629fd770 100644 --- a/SL/Auth.pm +++ b/SL/Auth.pm @@ -930,20 +930,6 @@ sub all_rights_full { my $locale = $main::locale; my @all_rights = ( - ["--crm", $locale->text("CRM optional software")], - ["crm_search", $locale->text("CRM search")], - ["crm_new", $locale->text("CRM create customers, vendors and contacts")], - ["crm_service", $locale->text("CRM services")], - ["crm_admin", $locale->text("CRM admin")], - ["crm_adminuser", $locale->text("CRM user")], - ["crm_adminstatus", $locale->text("CRM status")], - ["crm_email", $locale->text("CRM send email")], - ["crm_termin", $locale->text("CRM termin")], - ["crm_opportunity", $locale->text("CRM opportunity")], - ["crm_knowhow", $locale->text("CRM know how")], - ["crm_follow", $locale->text("CRM follow up")], - ["crm_notices", $locale->text("CRM notices")], - ["crm_other", $locale->text("CRM other")], ["--master_data", $locale->text("Master Data")], ["customer_vendor_edit", $locale->text("Create customers and vendors. Edit all vendors. Edit only customers where salesman equals employee (login)")], ["customer_vendor_all_edit", $locale->text("Create customers and vendors. Edit all vendors. Edit all customers")], diff --git a/SL/CT.pm b/SL/CT.pm index 44adb406c..cdf274b71 100644 --- 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')|; } } diff --git a/SL/ClientJS.pm b/SL/ClientJS.pm index 2eedbee07..d4775b830 100644 --- a/SL/ClientJS.pm +++ b/SL/ClientJS.pm @@ -118,6 +118,8 @@ my %supported_methods = ( reinit_widgets => 0, # kivi.reinit_widgets() run => -1, # kivi.run(, ) run_once_for => 3, # kivi.run_once_for(, ) + + scroll_into_view => 1, # $()[0].scrollIntoView() ); sub AUTOLOAD { diff --git a/SL/Clipboard.pm b/SL/Clipboard.pm index 6f30071b1..ee3d36343 100644 --- a/SL/Clipboard.pm +++ b/SL/Clipboard.pm @@ -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. 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 would be C. These support classes must inherit from L 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 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 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 is returned instead. diff --git a/SL/Clipboard/Base.pm b/SL/Clipboard/Base.pm index 47ccc2413..fc0a16a1f 100644 --- a/SL/Clipboard/Base.pm +++ b/SL/Clipboard/Base.pm @@ -180,13 +180,13 @@ L. =item C -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 itself: the primary key columns (if any) and the columns C and C (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 diff --git a/SL/Common.pm b/SL/Common.pm index 33d4d41cf..bcaeb62c1 100644 --- a/SL/Common.pm +++ b/SL/Common.pm @@ -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); diff --git a/SL/Controller/Inventory.pm b/SL/Controller/Inventory.pm index 8e5a445de..5bc780bf9 100644 --- a/SL/Controller/Inventory.pm +++ b/SL/Controller/Inventory.pm @@ -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}, }); diff --git a/SL/Controller/Project.pm b/SL/Controller/Project.pm index 2c2498b04..604978591 100644 --- a/SL/Controller/Project.pm +++ b/SL/Controller/Project.pm @@ -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 }), ); } diff --git a/SL/DB/AuthUser.pm b/SL/DB/AuthUser.pm index 1eac6f937..a8d8ab513 100644 --- a/SL/DB/AuthUser.pm +++ b/SL/DB/AuthUser.pm @@ -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 => { diff --git a/SL/DB/BackgroundJob.pm b/SL/DB/BackgroundJob.pm index 10e319ba3..3e6c03da5 100644 --- a/SL/DB/BackgroundJob.pm +++ b/SL/DB/BackgroundJob.pm @@ -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; } diff --git a/SL/DB/DeliveryOrder.pm b/SL/DB/DeliveryOrder.pm index b530db731..436c9b1a5 100644 --- a/SL/DB/DeliveryOrder.pm +++ b/SL/DB/DeliveryOrder.pm @@ -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 for compatibility with other sales/purchase models. -=item C - -Returns the delivery order items sorted by their ID (same order they -appear in the frontend delivery order masks). - =item C Creates a new C instance and copies as much diff --git a/SL/DB/Helper/ALL.pm b/SL/DB/Helper/ALL.pm index 878300693..fb9e833ca 100644 --- a/SL/DB/Helper/ALL.pm +++ b/SL/DB/Helper/ALL.pm @@ -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 index 000000000..77c5f19bc --- /dev/null +++ b/SL/DB/Helper/AttrSorted.pm @@ -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 + +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 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. + +=item * C is the name of the new function to create. It +defaults to the unsorted name postfixed with C<_sorted>. + +=item * C must be a function name to be called on the +objects to be sorted. It is supposed to return either C (no +position has been set yet) or a numeric value. Defaults to C. + +=back + +=back + +=head1 BUGS + +Nothing here yet. + +=head1 AUTHOR + +Moritz Bunkus Em.bunkus@linet-services.deE + +=cut diff --git a/SL/DB/Helper/Mappings.pm b/SL/DB/Helper/Mappings.pm index c29cc0a44..31ae7b699 100644 --- a/SL/DB/Helper/Mappings.pm +++ b/SL/DB/Helper/Mappings.pm @@ -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', diff --git a/SL/DB/Invoice.pm b/SL/DB/Invoice.pm index 3ed6cd34e..065d2b3cd 100644 --- a/SL/DB/Invoice.pm +++ b/SL/DB/Invoice.pm @@ -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 index 000000000..08657bde4 --- /dev/null +++ b/SL/DB/Letter.pm @@ -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 index 000000000..eb4a4b65f --- /dev/null +++ b/SL/DB/LetterDraft.pm @@ -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; diff --git a/SL/DB/MetaSetup/Default.pm b/SL/DB/MetaSetup/Default.pm index e566089f5..5671a4bb0 100644 --- a/SL/DB/MetaSetup/Default.pm +++ b/SL/DB/MetaSetup/Default.pm @@ -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 index 000000000..a04c4f159 --- /dev/null +++ b/SL/DB/MetaSetup/Letter.pm @@ -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 index 000000000..6b450d09d --- /dev/null +++ b/SL/DB/MetaSetup/LetterDraft.pm @@ -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; +; diff --git a/SL/DB/Order.pm b/SL/DB/Order.pm index 48e2be664..f73e32ca0 100644 --- a/SL/DB/Order.pm +++ b/SL/DB/Order.pm @@ -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; diff --git a/SL/DB/PriceRule.pm b/SL/DB/PriceRule.pm index e33b3a3e4..70b5ab4da 100644 --- a/SL/DB/PriceRule.pm +++ b/SL/DB/PriceRule.pm @@ -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; } diff --git a/SL/DB/PriceRuleItem.pm b/SL/DB/PriceRuleItem.pm index 3fc39dfb8..8aa71dab3 100644 --- a/SL/DB/PriceRuleItem.pm +++ b/SL/DB/PriceRuleItem.pm @@ -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; diff --git a/SL/DB/PurchaseInvoice.pm b/SL/DB/PurchaseInvoice.pm index ed0523bb4..ab1bd4957 100644 --- a/SL/DB/PurchaseInvoice.pm +++ b/SL/DB/PurchaseInvoice.pm @@ -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; diff --git a/SL/DN.pm b/SL/DN.pm index c9289e46f..55200835e 100644 --- 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); diff --git a/SL/DO.pm b/SL/DO.pm index 80b1ae352..e764ac6c3 100644 --- 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) diff --git a/SL/Dispatcher/AuthHandler/Admin.pm b/SL/Dispatcher/AuthHandler/Admin.pm index 9ef6ef388..06fd3cfe3 100644 --- a/SL/Dispatcher/AuthHandler/Admin.pm +++ b/SL/Dispatcher/AuthHandler/Admin.pm @@ -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'); diff --git a/SL/IC.pm b/SL/IC.pm index 27af46a50..e25e13e26 100644 --- 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 = <{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, diff --git a/SL/IR.pm b/SL/IR.pm index 208d2e4fb..8bd2f5f53 100644 --- 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) diff --git a/SL/OE.pm b/SL/OE.pm index 8f7c050d7..b8dd56072 100644 --- 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) diff --git a/SL/PriceSource.pm b/SL/PriceSource.pm index 89dffba89..c8cdc6cd1 100644 --- a/SL/PriceSource.pm +++ b/SL/PriceSource.pm @@ -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 diff --git a/SL/PriceSource/Business.pm b/SL/PriceSource/Business.pm index ca66c5dba..fc7b298fc 100644 --- a/SL/PriceSource/Business.pm +++ b/SL/PriceSource/Business.pm @@ -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), ) } diff --git a/SL/PriceSource/Discount.pm b/SL/PriceSource/Discount.pm index a7475eae2..ae4592bd9 100644 --- a/SL/PriceSource/Discount.pm +++ b/SL/PriceSource/Discount.pm @@ -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, diff --git a/SL/PriceSource/Pricegroup.pm b/SL/PriceSource/Pricegroup.pm index 8a322c5ac..2a8adae13 100644 --- a/SL/PriceSource/Pricegroup.pm +++ b/SL/PriceSource/Pricegroup.pm @@ -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; diff --git a/SL/PriceSource/Vendor.pm b/SL/PriceSource/Vendor.pm index 94d9bd01f..0b09ad698 100644 --- a/SL/PriceSource/Vendor.pm +++ b/SL/PriceSource/Vendor.pm @@ -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'), diff --git a/bin/mozilla/ar.pl b/bin/mozilla/ar.pl index 2960001fc..6a024a3cf 100644 --- a/bin/mozilla/ar.pl +++ b/bin/mozilla/ar.pl @@ -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}"; diff --git a/bin/mozilla/ct.pl b/bin/mozilla/ct.pl index 5a2dbd6c5..ec1ccbfba 100644 --- a/bin/mozilla/ct.pl +++ b/bin/mozilla/ct.pl @@ -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)), diff --git a/bin/mozilla/do.pl b/bin/mozilla/do.pl index 3f624181f..33ab98c01 100644 --- a/bin/mozilla/do.pl +++ b/bin/mozilla/do.pl @@ -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'); } diff --git a/bin/mozilla/ic.pl b/bin/mozilla/ic.pl index 76f086938..f83d42a53 100644 --- a/bin/mozilla/ic.pl +++ b/bin/mozilla/ic.pl @@ -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/\"/"/g } diff --git a/bin/mozilla/io.pl b/bin/mozilla/io.pl index e58457583..74784e603 100644 --- a/bin/mozilla/io.pl +++ b/bin/mozilla/io.pl @@ -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); } diff --git a/bin/mozilla/ir.pl b/bin/mozilla/ir.pl index 4cd96ca9f..5bd354816 100644 --- a/bin/mozilla/ir.pl +++ b/bin/mozilla/ir.pl @@ -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/\"/"/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; diff --git a/bin/mozilla/is.pl b/bin/mozilla/is.pl index cac93daad..a4f9e85b9 100644 --- a/bin/mozilla/is.pl +++ b/bin/mozilla/is.pl @@ -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; diff --git a/bin/mozilla/letter.pl b/bin/mozilla/letter.pl index 18564d3c8..d9a07423a 100755 --- a/bin/mozilla/letter.pl +++ b/bin/mozilla/letter.pl @@ -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}; diff --git a/bin/mozilla/oe.pl b/bin/mozilla/oe.pl index 147e9d9de..93e9f2a2f 100644 --- a/bin/mozilla/oe.pl +++ b/bin/mozilla/oe.pl @@ -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}; diff --git a/css/common.css b/css/common.css index fb8ec75a3..2594ef665 100644 --- a/css/common.css +++ b/css/common.css @@ -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; } diff --git a/doc/changelog b/doc/changelog index 5c9aaab46..8df983b29 100644 --- a/doc/changelog +++ b/doc/changelog @@ -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 diff --git a/index.html b/index.html index 5170e1d4a..71bb76600 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,7 @@ + kivitendo-Login diff --git a/js/client_js.js b/js/client_js.js index a0e6eab61..6d8981990 100644 --- a/js/client_js.js +++ b/js/client_js.js @@ -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]); diff --git a/locale/de/all b/locale/de/all index 97f3b5b95..dfc0f97c8 100755 --- a/locale/de/all +++ b/locale/de/all @@ -178,7 +178,6 @@ $self->{texts} = { 'Add User Group' => 'Neue Benutzergruppe', 'Add Vendor' => 'Lieferant erfassen', 'Add Vendor Invoice' => 'Einkaufsrechnung erfassen', - 'Add Vendor Letter' => '', 'Add Warehouse' => 'Lager erfassen', 'Add and edit units' => 'Einheiten erfassen und bearbeiten', 'Add bank account' => 'Bankkonto erfassen', @@ -1067,6 +1066,7 @@ $self->{texts} = { 'Edit requirement spec template' => 'Pflichtenheftvorlage bearbeiten', 'Edit requirement spec type' => 'Pflichtenhefttypen bearbeiten', 'Edit risk level' => 'Risikograd bearbeiten', + 'Edit sales letters' => 'Verkaufsbrief erstellen', 'Edit sales price rule' => 'Verkaufspreisregel bearbeiten', 'Edit section #1' => 'Abschnitt #1 bearbeiten', 'Edit taxzone' => 'Steuerzone bearbeiten', @@ -1394,6 +1394,7 @@ $self->{texts} = { 'Information' => 'Information', 'Initial version.' => 'Initiale Version.', 'Insert' => 'Einfügen', + 'Insert Date' => 'Erfassungsdatum', 'Insert new' => 'Hinzufügen', 'Insert with new customer/vendor number' => 'Mit neuer Kunden-/Lieferantennummer anlegen', 'Insert with new database ID' => 'Neu anlegen mit neuer Datenbank-ID', @@ -1454,6 +1455,7 @@ $self->{texts} = { 'It will simply set the taxkey to 0 (meaning "no taxes") which is the correct value for such inventory transactions.' => 'Es wird einfach die Steuerschlüssel auf 0 setzen, was "keine Steuer" bedeutet und für solche Warenbestandsbuchungen der richtige Wert ist.', 'Item deleted!' => 'Artikel gelöscht!', 'Item mode' => 'Artikelmodus', + 'Item multi selection with qty' => 'Artikel-Mehrfachauswahl mit Menge', 'Item not on file!' => 'Dieser Artikel ist nicht in der Datenbank!', 'Item values' => 'Artikelwerte', 'Item variables' => 'Artikelvariablen', @@ -1863,13 +1865,13 @@ $self->{texts} = { 'Paid' => 'bezahlt', 'Paid amount' => 'Bezahlter Betrag', 'Part' => 'Ware', + 'Part "#1" has chargenumber or best before date set. So it cannot be transfered automaticaly.' => 'Bei Artikel "#1" ist eine Chargenummer oder ein Mindesthaltbarkeitsdatum vergeben. Deshalb kann dieser Artikel nicht automatisch ausgelagert werden.', 'Part (database ID)' => 'Artikel (Datenbank-ID)', 'Part Description' => 'Artikelbeschreibung', 'Part Description missing!' => 'Artikelbezeichnung fehlt!', 'Part Notes' => 'Bemerkungen', 'Part Number' => 'Artikelnummer', 'Part Number missing!' => 'Artikelnummer fehlt!', - 'Part "#1" has chargenumber or best before date set. So it cannot be transfered automaticaly.' => 'Bei Artikel "#1" ist eine Chargenummer oder ein Mindesthaltbarkeitsdatum vergeben. Deshalb kann dieser Artikel nicht automatisch ausgelagert werden.', 'Part picker' => 'Artikelauswahl', 'Partial invoices' => 'Teilrechnungen', 'Partnumber' => 'Artikelnummer', @@ -1931,7 +1933,6 @@ $self->{texts} = { 'Picture #1: #2' => 'Abbildung #1: #2', 'Pictures for parts' => 'Bilder für Waren', 'Pictures for search parts' => 'Bilder für Warensuche', - 'Pirce rules must have at least one rule.' => 'Preisregeln brauchen mindestens eine Bedingung.', 'Please Check the bank information for each customer:' => 'Bitte überprüfen Sie die Bankinformationen der Kunden:', 'Please Check the bank information for each vendor:' => 'Bitte überprüfen Sie die Kontoinformationen der Lieferanten:', 'Please ask your administrator to create warehouses and bins.' => 'Bitten Sie Ihren Administrator, dass er Lager und Lagerplätze anlegt.', @@ -1940,6 +1941,7 @@ $self->{texts} = { 'Please contact your administrator or a service provider.' => 'Bitte kontaktieren Sie Ihren Administrator oder einen Dienstleister.', 'Please contact your administrator.' => 'Bitte wenden Sie sich an Ihren Administrator.', 'Please correct the settings and try again or deactivate that client.' => 'Bitte korrigieren Sie die Einstellungen und versuchen Sie es erneut, oder deaktivieren Sie diesen Mandanten.', + 'Please create/copy a template named letter.tex in your client template dir' => 'Bitte erstellen / kopieren Sie eine Druckvorlage namens letter.tex in Ihren Mandantenvorlagen-Ordner', 'Please define a taxkey for the following taxes and run the update again:' => 'Bitte definieren Sie einen Steuerschlüssel für die folgenden Steuern und starten Sie dann das Update erneut:', 'Please do so in the administration area.' => 'Bitte erledigen Sie dies im Administrationsbereich.', 'Please enter a profile name.' => 'Bitte geben Sie einen Profilnamen an.', @@ -2007,6 +2009,7 @@ $self->{texts} = { 'Price group (name)' => 'Preisgruppe (Name) ', 'Price information' => 'Preisinformation', 'Price or discount must not be zero.' => 'Preis/Rabatt darf nicht 0,00 sein', + 'Price rules must have at least one rule.' => 'Preisregeln brauchen mindestens eine Bedingung.', 'Price sources deactivated in this client' => 'Preisquellen die in diesem Mandanten deaktiviert sind', 'Price type explanation' => 'Preistyp Erklärung', 'Pricegroup' => 'Preisgruppe', @@ -2222,6 +2225,9 @@ $self->{texts} = { 'Row was linked to another record' => 'Zeile wurde über einen anderen Beleg verlinkt', 'Row was source for current record' => 'Zeile war Quelle für aktuellen Beleg', 'Rule Details' => 'Regel Details', + 'Rule for customer must not be empty' => 'Eine Kundenbedingung darf nicht leer sein', + 'Rule for part must not be empty' => 'Eine Warenbedingung darf nicht leer sein', + 'Rule for vendor must not be empty' => 'Eine Lieferantenbedingung darf nicht leer sein', 'Run JavaScript unit tests' => 'JavaScript-Unit-Tests ausführen', 'Run at' => 'Ausgeführt um', 'Run tests' => 'Tests ausführen', @@ -2412,6 +2418,7 @@ $self->{texts} = { 'Show parts' => 'Artikel anzeigen', 'Show requirement spec' => 'Pflichtenheft anzeigen', 'Show requirement spec template' => 'Pflichtenheftvorlage anzeigen', + 'Show sales letters report' => 'Verkaufsbrief anzeigen', 'Show settings' => 'Einstellungen anzeigen', 'Show the picture in the part form' => 'Bild in Warenmaske anzeigen', 'Show the pictures in the result for search parts' => 'Bilder in Suchergebnis für Stammdaten -> Berichte -> Waren anzeigen', @@ -2696,7 +2703,6 @@ $self->{texts} = { 'The following currencies have been used, but they are not defined:' => 'Die folgenden Währungen wurden benutzt, sind aber nicht ordnungsgemäß in der Datenbank eingetragen:', 'The following drafts have been saved and can be loaded.' => 'Die folgenden Entwürfe wurden gespeichert und können geladen werden.', 'The following groups are valid for this client' => 'Die folgenden Gruppen sind für diesen Mandanten gültig', - 'The following letter drafts have been saved and can be loaded.' => 'Die folgenden Briefentwürfe sind vorhanden und können geladen werden.', 'The following list has been generated automatically from existing users collapsing users with identical settings into a single entry.' => 'Die folgende Liste wurde automatisch aus den im System vorhandenen Benutzern zusammengestellt, wobei identische Einstellungen zu einem Eintrag zusammengefasst wurden.', 'The following old files whose settings have to be merged manually into the new configuration file "config/kivitendo.conf" still exist:' => 'Es existieren noch die folgenden alten Dateien, deren Einstellungen manuell in die neue Konfiguratsdatei "config/kivitendo.conf" migriert werden müssen:', 'The following transaction contains wrong taxes:' => 'Die folgende Buchung enthält falsche Steuern:', @@ -3092,7 +3098,6 @@ $self->{texts} = { 'Vendor Discount' => 'Lieferantenrabatt', 'Vendor Invoice' => 'Einkaufsrechnung', 'Vendor Invoices & AP Transactions' => 'Einkaufsrechnungen & Kreditorenbuchungen', - 'Vendor Letters' => '', 'Vendor Name' => 'Lieferantenname', 'Vendor Number' => 'Lieferantennummer', 'Vendor Order Number' => 'Bestellnummer beim Lieferanten', @@ -3195,7 +3200,6 @@ $self->{texts} = { 'You have to grant users access to one or more clients.' => 'Benutzern muss dann Zugriff auf einzelne Mandanten gewährt werden.', 'You have to specify a department.' => 'Sie müssen eine Abteilung wählen.', 'You have to specify an execution date for each antry.' => 'Sie müssen für jeden zu buchenden Eintrag ein Ausführungsdatum angeben.', - 'You must choose a user.' => '', 'You must chose a user.' => 'Sie müssen einen Benutzer auswählen.', 'You must enter a name for your new print templates.' => 'Sie müssen einen Namen für die neuen Druckvorlagen angeben.', 'You must select existing print templates or create a new set.' => 'Sie müssen vorhandene Druckvorlagen auswählen oder einen neuen Satz anlegen.', @@ -3243,7 +3247,6 @@ $self->{texts} = { 'bis' => 'bis', 'building data' => 'Verarbeite Daten', 'building report' => 'Erstelle Bericht', - 'button' => '', 'cash' => 'Ist-Versteuerung', 'chargenumber #1' => 'Chargennummer #1', 'chart_of_accounts' => 'kontenuebersicht', diff --git a/locale/de/special_chars b/locale/de/special_chars index d542caba0..0d34668d4 100644 --- a/locale/de/special_chars +++ b/locale/de/special_chars @@ -22,7 +22,7 @@ order=< > \n \n=
[Template/LaTeX] -order=\\ & \n \r " $ % _ # ^ { } < > £ ± ² ³ ° § ® © \xad \xa0 ➔ → ← | +order=\\ & \n \r " $ % _ # ^ { } < > £ ± ² ³ ° § ® © \xad \xa0 ➔ → ← | − \\=\\textbackslash\s = "='' @@ -55,6 +55,7 @@ _=\\_ ←=$\\leftarrow$ \xa0=~ |=\\textbar +−={\\textemdash} [Template/OpenDocument] order=& < > " ' \x80 \n \r diff --git a/locale/en/all b/locale/en/all index d3f54980f..2bdde42d9 100644 --- a/locale/en/all +++ b/locale/en/all @@ -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/scripts/installation_check.pl b/scripts/installation_check.pl index 1b645733c..43b290a35 100755 --- a/scripts/installation_check.pl +++ b/scripts/installation_check.pl @@ -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 index 000000000..4f40139e3 --- /dev/null +++ b/sql/Pg-upgrade2/ar_ap_fix_notes_as_html_for_non_invoices.pl @@ -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 = <dbh, $query)) { + next if !$row->{notes} || (($row->{notes} !~ m{^<[a-z]+>}) && ($row->{notes} !~ m{$})); + + my $new_content = $row->{notes}; + $new_content =~ s{^

|

$}{}gi; + $new_content =~ s{
}{\n}gi; + $new_content =~ s{

}{\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; diff --git a/sql/Pg-upgrade2/convert_taxzone.pl b/sql/Pg-upgrade2/convert_taxzone.pl index b1e1a0bb6..3343c6f12 100644 --- a/sql/Pg-upgrade2/convert_taxzone.pl +++ b/sql/Pg-upgrade2/convert_taxzone.pl @@ -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 = < dois).
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);"
Hintergrund: Eingelagerte Lieferscheine können / sollen nicht gelöscht werden, allerdings weist dieser Datenbestand genau diesen Fall auf.
--- @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); diff --git a/t/006spellcheck.t b/t/006spellcheck.t index ae42e8f7f..e741cdd62 100644 --- a/t/006spellcheck.t +++ b/t/006spellcheck.t @@ -47,6 +47,7 @@ puhs sekf sucess varsion +pirce ); $testcount = scalar(@Support::Files::testitems); diff --git a/templates/webpages/am/config.html b/templates/webpages/am/config.html index 13b060a16..ac7875b11 100644 --- a/templates/webpages/am/config.html +++ b/templates/webpages/am/config.html @@ -78,6 +78,13 @@ + + [% 'Item multi selection with qty' | $T8 %] + + [% L.yes_no_tag('item_multiselect', myconfig_item_multiselect) %] + + + diff --git a/templates/webpages/ar/search.html b/templates/webpages/ar/search.html index 0790e2c85..b43630fad 100644 --- a/templates/webpages/ar/search.html +++ b/templates/webpages/ar/search.html @@ -115,6 +115,15 @@ [% L.date_tag('transdateto') %] + +[%- IF CT_CUSTOM_VARIABLES.size %] + + + [% 'Custom variables for module' | $T8 %]: [%'Customers and vendors' | $T8 %] + + [% CT_CUSTOM_VARIABLES_FILTER_CODE %] +[%- END %] + @@ -213,6 +222,13 @@ [% 'USt-IdNr.' | $T8 %] + + + +
+ [% CT_CUSTOM_VARIABLES_INCLUSION_CODE %] +
+ diff --git a/templates/webpages/ct/search.html b/templates/webpages/ct/search.html index 933a7d1c0..aa517079f 100644 --- a/templates/webpages/ct/search.html +++ b/templates/webpages/ct/search.html @@ -66,6 +66,17 @@ [% END %] + + [% 'Insert Date' | $T8 %] [% 'From' | $T8 %] + + [% L.date_tag('insertdatefrom') %] + + [% 'Bis' | $T8 %] + + [% L.date_tag('insertdateto') %] + + + [% CUSTOM_VARIABLES_FILTER_CODE %] @@ -86,7 +97,7 @@ [% 'Include in Report' | $T8 %] - + + [% IF IS_CUSTOMER %] + + + [% END %] diff --git a/templates/webpages/do/form_header.html b/templates/webpages/do/form_header.html index 9b4e9f26f..2a28853d5 100644 --- a/templates/webpages/do/form_header.html +++ b/templates/webpages/do/form_header.html @@ -314,6 +314,13 @@ + [%- IF is_customer %] + + + + + [%- END %] + + [%- IF ALL_BUSINESS_TYPES.size %] + + + + + [%- END %] + + [%- IF is_customer %] + + + + + + + [%- END %] + + [% IF is_customer %] + + [%- END %] diff --git a/templates/webpages/ic/search.html b/templates/webpages/ic/search.html index 4433c82a1..8e71cb659 100644 --- a/templates/webpages/ic/search.html +++ b/templates/webpages/ic/search.html @@ -65,6 +65,19 @@ + + + + + + + + + + [% CUSTOM_VARIABLES_FILTER_CODE %] [%- IF is_assembly %] @@ -208,6 +221,8 @@ + + [% CUSTOM_VARIABLES_INCLUSION_CODE %] diff --git a/templates/webpages/io/select_item.html b/templates/webpages/io/select_item.html index 9252b45fc..11dfbf609 100644 --- a/templates/webpages/io/select_item.html +++ b/templates/webpages/io/select_item.html @@ -5,7 +5,11 @@
@@ -172,11 +183,21 @@ + + +
+ + +
[% 'Insert Date' | $T8 %][% insertdate %]
[% 'Project Number' | $T8 %] diff --git a/templates/webpages/do/search.html b/templates/webpages/do/search.html index e84bbf3e4..baa00bb18 100644 --- a/templates/webpages/do/search.html +++ b/templates/webpages/do/search.html @@ -9,6 +9,8 @@ [%- SET is_customer = '0' %] [%- END %] + [%- SET vctypelabel = vc == 'customer' ? LxERP.t8('Customer type') : LxERP.t8('Vendor type') %] + @@ -108,6 +110,15 @@
[% vctypelabel %] + [% L.select_tag('business_id', ALL_BUSINESS_TYPES, title_key = 'description', with_empty = 1, style='width:250px') %] +
[% 'Delivery Order Date' | $T8 %] [% 'From' | $T8 %] @@ -130,6 +141,19 @@
[% 'Insert Date' | $T8 %] [% 'From' | $T8 %] + [% L.date_tag('insertdatefrom') %] + [% 'Bis' | $T8 %] + [% L.date_tag('insertdateto') %] +
[% 'Include in Report' | $T8 %] @@ -188,6 +212,12 @@ + + +
[% 'Shopartikel' | $T8 %][% L.yes_no_tag('shop', shop, default='', with_empty=1, empty_title='---') %]
[% 'Insert Date' | $T8 %] + [% 'From' | $T8 %][% L.date_tag('insertdatefrom') %] + [% 'Bis' | $T8 %] [% L.date_tag('insertdateto') %] +
[%- L.checkbox_tag('l_notes', label=LxERP.t8('Notes'), value='Y') %] [%- L.checkbox_tag('l_name', label=LxERP.t8('Name in Selected Records'), value='Y') %][%- L.checkbox_tag('l_shop', label=LxERP.t8('Shopartikel'), value='Y') %][%- L.checkbox_tag('l_insertdate', label=LxERP.t8('Insert Date'), value='Y') %]
- + [%- IF myconfig_item_multiselect %] + + [%- ELSE %] + + [%- END %] @@ -19,7 +23,11 @@ [%- FOREACH item = ITEM_LIST %] - + [%- IF myconfig_item_multiselect %] + + [%- ELSE %] + + [%- END %] @@ -41,3 +49,22 @@ [% L.submit_tag('action', LxERP.t8('Continue')) %] + +[%- IF myconfig_item_multiselect %] + +[%- END %] diff --git a/templates/webpages/ir/form_header.html b/templates/webpages/ir/form_header.html index 5a8cb024a..362bd8957 100644 --- a/templates/webpages/ir/form_header.html +++ b/templates/webpages/ir/form_header.html @@ -80,6 +80,12 @@ [% LxERP.format_amount(creditremaining,0 ,'0') %] +[%- IF business %] + + + + +[%- END %] diff --git a/templates/webpages/letter/edit.html b/templates/webpages/letter/edit.html index 39aa7373f..bf91648d6 100644 --- a/templates/webpages/letter/edit.html +++ b/templates/webpages/letter/edit.html @@ -184,8 +184,12 @@ - - + +[%- IF letter.letternumber %] + + +[% END %] + diff --git a/templates/webpages/oe/form_header.html b/templates/webpages/oe/form_header.html index 81ed919af..773c0894a 100644 --- a/templates/webpages/oe/form_header.html +++ b/templates/webpages/oe/form_header.html @@ -233,6 +233,12 @@ [% L.date_tag('reqdate', reqdate, id='reqdate') %] + [%- IF is_sales_ord %] + + + + + [%- END %] + + [%- IF type == 'sales_order' %] + + + + + + + [%- END %] + [%- IF type == 'sales_quotation' %] @@ -145,6 +159,15 @@ [%- END %] + +[%- IF CT_CUSTOM_VARIABLES.size %] + + + + + [% CT_CUSTOM_VARIABLES_FILTER_CODE %] +[%- END %] + + [%- IF type == 'sales_order' %] + + [%- END %] + + [% CT_CUSTOM_VARIABLES_INCLUSION_CODE %] + [%- IF type == 'sales_order' %] [%- END %]
 [% LxERP.t8('Qty') %] [% LxERP.t8('Number') %] [% LxERP.t8('Part Description') %] [% LxERP.t8('Other Matches') %]
[% L.input_tag('select_qty_' _ HTML.escape(item.id), '', size => 5) %][% HTML.escape(item.partnumber) %] [% HTML.escape(item.description) %] [% HTML.escape(item.matches).join('
') %]
[% 'Vendor type' | $T8 %][% business %]; [% 'Trade Discount' | $T8 %] [% LxERP.format_amount(tradediscount * 100) %] %
[% 'Record in' | $T8 %]
[% 'Insert Date' | $T8 %][% insertdate %]
[% 'Project Number' | $T8 %] diff --git a/templates/webpages/oe/search.html b/templates/webpages/oe/search.html index 06f1429c7..323fd2383 100644 --- a/templates/webpages/oe/search.html +++ b/templates/webpages/oe/search.html @@ -126,6 +126,20 @@ [% L.date_tag('reqdateto') %]
[% 'Insert Date' | $T8 %] [% 'From' | $T8 %] + [% L.date_tag('insertdatefrom') %] + [% 'Bis' | $T8 %] + [% L.date_tag('insertdateto') %] +
[% 'Expected billing date' | $T8 %] [% 'From' | $T8 %]
[% 'Custom variables for module' | $T8 %]: [%'Customers and vendors' | $T8 %]
[% 'Include in Report' | $T8 %] @@ -206,6 +229,12 @@ + + +
@@ -308,6 +337,9 @@