my $query =
qq|SELECT DISTINCT a.id, a.invnumber, a.ordnumber, a.cusordnumber, a.transdate, | .
+ qq| a.donumber, a.deliverydate, | .
qq| a.duedate, a.netamount, a.amount, a.paid, | .
qq| a.invoice, a.datepaid, a.notes, a.shipvia, | .
qq| a.shippingpoint, a.storno, a.storno_id, a.globalproject_id, | .
my $sortdir = !defined $form->{sortdir} ? 'ASC' : $form->{sortdir} ? 'ASC' : 'DESC';
my $sortorder = join(', ', map { "$_ $sortdir" } @a);
- if (grep({ $_ eq $form->{sort} } qw(id transdate duedate invnumber ordnumber cusordnumber name datepaid employee shippingpoint shipvia transaction_description department))) {
+ if (grep({ $_ eq $form->{sort} } qw(id transdate duedate invnumber ordnumber cusordnumber donumber deliverydate name datepaid employee shippingpoint shipvia transaction_description department))) {
$sortorder = $form->{sort} . " $sortdir";
}
run_once_for => 3, # kivi.run_once_for(<TARGET>, <ARGS>)
scroll_into_view => 1, # $(<TARGET>)[0].scrollIntoView()
+
+ set_cursor_position => 2, # kivi.set_cursor_position(<TARGET>, <ARGS>)
);
my %trim_target_for = map { ($_ => 1) } qw(insertAfter insertBefore appendTo prependTo);
$self->controller->track_progress(progress => 10);
- my $old_numberformat = $::myconfig{numberformat};
- $::myconfig{numberformat} = $self->controller->profile->get('numberformat');
+ local $::myconfig{numberformat} = $self->controller->profile->get('numberformat');
+ local $::myconfig{dateformat} = $self->controller->profile->get('dateformat');
$self->csv->parse;
$self->fix_field_lengths;
$self->controller->track_progress(progress => 100);
-
- $::myconfig{numberformat} = $old_numberformat;
}
sub add_columns {
}
}
- $entry->{part}->makemodels([ $entry->{part}->makemodels_sorted, @new_makemodels ]) if @new_makemodels;
+ $entry->{part}->makemodels([ @{$entry->{part}->makemodels_sorted}, @new_makemodels ]) if @new_makemodels;
# reindex makemodels
my $i = 0;
my $db = SL::DB::Invoice->new->db;
my @invoices;
+ my @already_closed_delivery_orders;
if (!$db->with_transaction(sub {
foreach my $id (@sales_delivery_order_ids) {
my $delivery_order = SL::DB::DeliveryOrder->new(id => $id)->load;
- my $invoice = $delivery_order->convert_to_invoice() || die $db->error;
- push @invoices, $invoice;
+ # Only process open delivery orders. In this list should only be open
+ # delivery orders, but if the user clicked browser back, a new creation
+ # of invoices for delivery orders which are closed now can be triggered.
+ # Prevent this.
+ if ($delivery_order->closed) {
+ push @already_closed_delivery_orders, $delivery_order;
+
+ } else {
+ my $invoice = $delivery_order->convert_to_invoice() || die $db->error;
+ push @invoices, $invoice;
+ }
}
1;
my $key = sprintf('%d-%d', Time::HiRes::gettimeofday());
$::auth->set_session_value("MassInvoiceCreatePrint::ids-${key}" => [ map { $_->id } @invoices ]);
- flash_later('info', t8('The invoices have been created. They\'re pre-selected below.'));
+ if (@already_closed_delivery_orders) {
+ my $dos_list = join ' ', map { $_->donumber } @already_closed_delivery_orders;
+ flash_later('error', t8('The following delivery orders could not be processed because they are already closed: #1', $dos_list));
+ }
+
+ flash_later('info', t8('The invoices have been created. They\'re pre-selected below.')) if @invoices;
+
$self->redirect_to(action => 'list_invoices', ids => $key);
}
if ($::form->{ids}) {
my $key = 'MassInvoiceCreatePrint::ids-' . $::form->{ids};
$self->invoice_ids($::auth->get_session_value($key) || []);
+
+ # Prevent models->get to retrieve any invoices if session key is there
+ # but no ids are given.
+ $self->invoice_ids([0]) if !@{$self->invoice_ids};
+
$self->invoice_models->add_additional_url_params(ids => $::form->{ids});
}
],
action => [
t8("Create and print invoices for all selected delivery orders"),
- call => [ 'kivi.MassInvoiceCreatePrint.submitMassCreationForm' ],
- disabled => !$params{num_rows} ? $::locale->text('The report doesn\'t contain entries.') : undef,
- only_if => $params{show_creation_buttons},
+ submit => [ 'form', { action => 'MassInvoiceCreatePrint/create_invoices' } ],
+ disabled => !$params{num_rows} ? $::locale->text('The report doesn\'t contain entries.') : undef,
+ only_if => $params{show_creation_buttons},
+ checks => [ 'kivi.MassInvoiceCreatePrint.checkDeliveryOrderSelection' ],
+ only_once => 1,
],
action => [
my $formname = $::form->{print_options}->{formname};
my $copies = $::form->{print_options}->{copies};
my $groupitems = $::form->{print_options}->{groupitems};
+ my $printer_id = $::form->{print_options}->{printer_id};
# only pdf and opendocument by now
if (none { $format eq $_ } qw(pdf opendocument opendocument_pdf)) {
my @errors = generate_pdf($self->order, \$pdf, { format => $format,
formname => $formname,
language => $self->order->language,
+ printer_id => $printer_id,
groupitems => $groupitems });
if (scalar @errors) {
return $self->js->flash('error', t8('Conversion to PDF failed: #1', $errors[0]))->render;
format => $::form->{print_options}->{format},
formname => $::form->{print_options}->{formname},
language => $self->order->language,
+ printer_id => $::form->{print_options}->{printer_id},
groupitems => $::form->{print_options}->{groupitems}});
if (scalar @errors) {
return $self->js->flash('error', t8('Conversion to PDF failed: #1', $errors[0]))->render($self);
sub action_save_and_delivery_order {
my ($self) = @_;
- my $errors = $self->save();
-
- if (scalar @{ $errors }) {
- $self->js->flash('error', $_) foreach @{ $errors };
- return $self->js->render();
- }
-
- my $text = $self->type eq sales_order_type() ? $::locale->text('The order has been saved')
- : $self->type eq purchase_order_type() ? $::locale->text('The order has been saved')
- : $self->type eq sales_quotation_type() ? $::locale->text('The quotation has been saved')
- : $self->type eq request_quotation_type() ? $::locale->text('The rfq has been saved')
- : '';
- flash_later('info', $text);
-
- my @redirect_params = (
+ $self->save_and_redirect_to(
controller => 'oe.pl',
action => 'oe_delivery_order_from_order',
- id => $self->order->id,
);
-
- $self->redirect_to(@redirect_params);
}
# save the order and redirect to the frontend subroutine for a new
sub action_save_and_invoice {
my ($self) = @_;
- my $errors = $self->save();
-
- if (scalar @{ $errors }) {
- $self->js->flash('error', $_) foreach @{ $errors };
- return $self->js->render();
- }
-
- my $text = $self->type eq sales_order_type() ? $::locale->text('The order has been saved')
- : $self->type eq purchase_order_type() ? $::locale->text('The order has been saved')
- : $self->type eq sales_quotation_type() ? $::locale->text('The quotation has been saved')
- : $self->type eq request_quotation_type() ? $::locale->text('The rfq has been saved')
- : '';
- flash_later('info', $text);
-
- my @redirect_params = (
+ $self->save_and_redirect_to(
controller => 'oe.pl',
action => 'oe_invoice_from_order',
- id => $self->order->id,
);
-
- $self->redirect_to(@redirect_params);
}
# workflow from sales quotation to sales order
sub action_save_and_ap_transaction {
my ($self) = @_;
- my $errors = $self->save();
-
- if (scalar @{ $errors }) {
- $self->js->flash('error', $_) foreach @{ $errors };
- return $self->js->render();
- }
-
- my $text = $self->type eq sales_order_type() ? $::locale->text('The order has been saved')
- : $self->type eq purchase_order_type() ? $::locale->text('The order has been saved')
- : $self->type eq sales_quotation_type() ? $::locale->text('The quotation has been saved')
- : $self->type eq request_quotation_type() ? $::locale->text('The rfq has been saved')
- : '';
- flash_later('info', $text);
-
- my @redirect_params = (
+ $self->save_and_redirect_to(
controller => 'ap.pl',
action => 'add_from_purchase_order',
- id => $self->order->id,
);
-
- $self->redirect_to(@redirect_params);
}
# set form elements in respect to a changed customer or vendor
$print_form->{format} = $params->{format} || 'pdf';
$print_form->{media} = $params->{media} || 'file';
$print_form->{groupitems} = $params->{groupitems};
+ $print_form->{printer_id} = $params->{printer_id};
$print_form->{media} = 'file' if $print_form->{media} eq 'screen';
$order->language($params->{language});
extension => $template_ext,
email => $print_form->{media} eq 'email',
language => $params->{language},
- printer_id => $print_form->{printer_id}, # todo
+ printer_id => $print_form->{printer_id},
);
if (!defined $template_file) {
: '';
}
+sub save_and_redirect_to {
+ my ($self, %params) = @_;
+
+ my $errors = $self->save();
+
+ if (scalar @{ $errors }) {
+ $self->js->flash('error', $_) foreach @{ $errors };
+ return $self->js->render();
+ }
+
+ my $text = $self->type eq sales_order_type() ? $::locale->text('The order has been saved')
+ : $self->type eq purchase_order_type() ? $::locale->text('The order has been saved')
+ : $self->type eq sales_quotation_type() ? $::locale->text('The quotation has been saved')
+ : $self->type eq request_quotation_type() ? $::locale->text('The rfq has been saved')
+ : '';
+ flash_later('info', $text);
+
+ $self->redirect_to(%params, id => $self->order->id);
+}
+
1;
__END__
=item * check for direct delivery (workflow sales order -> purchase order)
-=item * language / part translations
-
=item * access rights
=item * display weights
escape_char => '"',
charset => 'CP850',
numberformat => $::myconfig{numberformat},
+ dateformat => $::myconfig{dateformat},
duplicates => 'no_check',
);
my $as_minutes = "${attribute}_as_minutes";
return defined($self->$attribute) ? sprintf('%d:%02d', $self->$as_hours, $self->$as_minutes) : undef;
};
+
+ *{ $package . '::' . $attribute . '_in_hours' } = sub {
+ my ($self, $value) = @_;
+
+ $self->$attribute(int($value * 60 + 0.5)) if @_ > 1;
+ return $self->$attribute / 60.0;
+ };
+
+ *{ $package . '::' . $attribute . '_in_hours_as_number' } = sub {
+ my ($self, $value) = @_;
+
+ my $sub = "${attribute}_in_hours";
+
+ $self->$sub($::form->parse_amount(\%::myconfig, $value)) if @_ > 1;
+ return $::form->format_amount(\%::myconfig, $self->$sub, 2);
+ };
}
1;
e.g. C<1:30> for the value 90 minutes. Parsing such a string is
supported, too.
+=item C<attribute_in_hours [$new_value]>
+
+Access the full value but convert to and from hours when
+reading/writing the value.
+
+=item C<attribute_in_hours_as_number [$new_value]>
+
+Access the full value but convert to and from hours when
+reading/writing the value. The value is formatted to/parsed from the
+user's number format.
+
=back
=head1 FUNCTIONS
use Rose::DB::Object;
use Rose::DB::Object::Constants qw();
use List::MoreUtils qw(any pairwise);
+use List::Util qw(first);
use SL::DB;
use SL::DB::Helper::Attr;
return $self;
}
+sub update_collection {
+ my ($self, $attribute, $entries) = @_;
+
+ my $self_primary_key = "" . ($self->meta->primary_key_columns)[0];
+
+ croak "\$self hasn't been saved yet" if !$self->$self_primary_key;
+
+ my $relationship = first { $_->name eq $attribute } @{ $self->meta->relationships };
+
+ croak "No relationship found for attribute '$attribute'" if !$relationship;
+
+ my @primary_key_columns = $relationship->class->meta->primary_key_columns;
+
+ croak "Classes with multiple primary key columns are not supported" if scalar(@primary_key_columns) > 1;
+
+ my $class = $relationship->class;
+ my $manager_class = "SL::DB::Manager::" . substr($class, 8);
+ my $other_primary_key = "" . $primary_key_columns[0];
+ my $column_map = $relationship->column_map;
+ my @new_entries = @{ $entries // [] };
+ my @existing_entries = @{ $self->$attribute // [] };
+ my @to_delete = grep { my $value = $_->$other_primary_key; !any { $_->{$other_primary_key} == $value } @new_entries } @existing_entries;
+
+ $_->delete for @to_delete;
+
+ foreach my $entry (@new_entries) {
+ if (!$entry->{$other_primary_key}) {
+ my $new_instance = $class->new(%{ $entry });
+
+ foreach my $self_attribute (keys %{ $column_map }) {
+ my $other_attribute = $column_map->{$self_attribute};
+ $new_instance->$other_attribute($self->$self_attribute);
+ }
+
+ $new_instance->save;
+
+ next;
+ }
+
+ my $existing = first { $_->$other_primary_key == $entry->{$other_primary_key} } @existing_entries;
+ $existing->update_attributes(%{ $entry }) if $existing;
+ }
+}
+
sub call_sub {
my $self = shift;
my $sub = shift;
C<assign_attributes> function and saves the object afterwards. Returns
the object itself.
+=item C<update_collection $attribute, $entries, %params>
+
+Updates a one-to-many relationship named C<$attribute> to match the
+entries in C<$entries>. C<$entries> is supposed to be an array ref of
+hash refs.
+
+For each hash ref in C<$entries> that does not contain a field for the
+relationship's primary key column, this function creates a new entry
+in the database with its attributes set to the data in the entry.
+
+For each hash ref in C<$entries> that contains a field for the
+relationship's primary key column, this function looks up the
+corresponding entry in C<$self->$attribute> & updates its
+attributes with the data in the entry.
+
+All objects in C<$self->$attribute> for which no corresponding
+entry exists in C<$entries> are deleted by calling the object's
+C<delete> method.
+
+In all cases the relationship itself C<$self->$attribute> is not
+changed.
+
=item _get_manager_class
Returns the manager package for the object or class that it is called
my $session_cookie_value = $main::auth->get_session_id();
if ($session_cookie_value) {
- $session_cookie = $cgi->cookie('-name' => $main::auth->get_session_cookie_name(),
- '-value' => $session_cookie_value,
- '-path' => $uri->path,
- '-secure' => $::request->is_https);
+ $session_cookie = $cgi->cookie('-name' => $main::auth->get_session_cookie_name(),
+ '-value' => $session_cookie_value,
+ '-path' => $uri->path,
+ '-expires' => '+' . $::auth->{session_timeout} . 'm',
+ '-secure' => $::request->is_https);
}
}
--- /dev/null
+package SL::Helper::Inventory;
+
+use strict;
+use Carp;
+use DateTime;
+use Exporter qw(import);
+use List::Util qw(min sum);
+use List::UtilsBy qw(sort_by);
+use List::MoreUtils qw(any);
+use POSIX qw(ceil);
+
+use SL::Locale::String qw(t8);
+use SL::MoreCommon qw(listify);
+use SL::DBUtils qw(selectall_hashref_query selectrow_query);
+use SL::DB::TransferType;
+use SL::Helper::Number qw(_format_number _round_number);
+use SL::Helper::Inventory::Allocation;
+use SL::X;
+
+our @EXPORT_OK = qw(get_stock get_onhand allocate allocate_for_assembly produce_assembly check_constraints);
+our %EXPORT_TAGS = (ALL => \@EXPORT_OK);
+
+sub _get_stock_onhand {
+ my (%params) = @_;
+
+ my $onhand_mode = !!$params{onhand};
+
+ my @selects = (
+ 'SUM(qty) AS qty',
+ 'MIN(EXTRACT(epoch FROM inventory.itime)) AS itime',
+ );
+ my @values;
+ my @where;
+ my @groups;
+
+ if ($params{part}) {
+ my @ids = map { ref $_ ? $_->id : $_ } listify($params{part});
+ push @where, sprintf "parts_id IN (%s)", join ', ', ("?") x @ids;
+ push @values, @ids;
+ }
+
+ if ($params{bin}) {
+ my @ids = map { ref $_ ? $_->id : $_ } listify($params{bin});
+ push @where, sprintf "bin_id IN (%s)", join ', ', ("?") x @ids;
+ push @values, @ids;
+ }
+
+ if ($params{warehouse}) {
+ my @ids = map { ref $_ ? $_->id : $_ } listify($params{warehouse});
+ push @where, sprintf "warehouse.id IN (%s)", join ', ', ("?") x @ids;
+ push @values, @ids;
+ }
+
+ if ($params{chargenumber}) {
+ my @ids = listify($params{chargenumber});
+ push @where, sprintf "chargenumber IN (%s)", join ', ', ("?") x @ids;
+ push @values, @ids;
+ }
+
+ if ($params{date}) {
+ Carp::croak("not DateTime ".$params{date}) unless ref($params{date}) eq 'DateTime';
+ push @where, sprintf "shippingdate <= ?";
+ push @values, $params{date};
+ }
+
+ if (!$params{bestbefore} && $onhand_mode && default_show_bestbefore()) {
+ $params{bestbefore} = DateTime->now_local;
+ }
+
+ if ($params{bestbefore}) {
+ Carp::croak("not DateTime ".$params{date}) unless ref($params{bestbefore}) eq 'DateTime';
+ push @where, sprintf "(bestbefore IS NULL OR bestbefore >= ?)";
+ push @values, $params{bestbefore};
+ }
+
+ # by
+ my %allowed_by = (
+ part => [ qw(parts_id) ],
+ bin => [ qw(bin_id inventory.warehouse_id)],
+ warehouse => [ qw(inventory.warehouse_id) ],
+ chargenumber => [ qw(chargenumber) ],
+ bestbefore => [ qw(bestbefore) ],
+ for_allocate => [ qw(parts_id bin_id inventory.warehouse_id chargenumber bestbefore) ],
+ );
+
+ if ($params{by}) {
+ for (listify($params{by})) {
+ my $selects = $allowed_by{$_} or Carp::croak("unknown option for by: $_");
+ push @selects, @$selects;
+ push @groups, @$selects;
+ }
+ }
+
+ my $select = join ',', @selects;
+ my $where = @where ? 'WHERE ' . join ' AND ', @where : '';
+ my $group_by = @groups ? 'GROUP BY ' . join ', ', @groups : '';
+
+ my $query = <<"";
+ SELECT $select FROM inventory
+ LEFT JOIN bin ON bin_id = bin.id
+ LEFT JOIN warehouse ON bin.warehouse_id = warehouse.id
+ $where
+ $group_by
+
+ if ($onhand_mode) {
+ $query .= ' HAVING SUM(qty) > 0';
+ }
+
+ my $results = selectall_hashref_query($::form, SL::DB->client->dbh, $query, @values);
+
+ my %with_objects = (
+ part => 'SL::DB::Manager::Part',
+ bin => 'SL::DB::Manager::Bin',
+ warehouse => 'SL::DB::Manager::Warehouse',
+ );
+
+ my %slots = (
+ part => 'parts_id',
+ bin => 'bin_id',
+ warehouse => 'warehouse_id',
+ );
+
+ if ($params{by} && $params{with_objects}) {
+ for my $with_object (listify($params{with_objects})) {
+ Carp::croak("unknown with_object $with_object") if !exists $with_objects{$with_object};
+
+ my $manager = $with_objects{$with_object};
+ my $slot = $slots{$with_object};
+ next if !(my @ids = map { $_->{$slot} } @$results);
+ my $objects = $manager->get_all(query => [ id => \@ids ]);
+ my %objects_by_id = map { $_->id => $_ } @$objects;
+
+ $_->{$with_object} = $objects_by_id{$_->{$slot}} for @$results;
+ }
+ }
+
+ if ($params{by}) {
+ return $results;
+ } else {
+ return $results->[0]{qty};
+ }
+}
+
+sub get_stock {
+ _get_stock_onhand(@_, onhand => 0);
+}
+
+sub get_onhand {
+ _get_stock_onhand(@_, onhand => 1);
+}
+
+sub allocate {
+ my (%params) = @_;
+
+ croak('allocate needs a part') unless $params{part};
+ croak('allocate needs a qty') unless $params{qty};
+
+ my $part = $params{part};
+ my $qty = $params{qty};
+
+ return () if $qty <= 0;
+
+ my $results = get_stock(part => $part, by => 'for_allocate');
+ my %bin_whitelist = map { (ref $_ ? $_->id : $_) => 1 } grep defined, listify($params{bin});
+ my %wh_whitelist = map { (ref $_ ? $_->id : $_) => 1 } grep defined, listify($params{warehouse});
+ my %chargenumbers = map { (ref $_ ? $_->id : $_) => 1 } grep defined, listify($params{chargenumber});
+
+ # sort results so that chargenumbers are matched first, then wanted bins, then wanted warehouses
+ my @sorted_results = sort {
+ exists $chargenumbers{$b->{chargenumber}} <=> exists $chargenumbers{$a->{chargenumber}} # then prefer wanted chargenumbers
+ || exists $bin_whitelist{$b->{bin_id}} <=> exists $bin_whitelist{$a->{bin_id}} # then prefer wanted bins
+ || exists $wh_whitelist{$b->{warehouse_id}} <=> exists $wh_whitelist{$a->{warehouse_id}} # then prefer wanted bins
+ || $a->{itime} <=> $b->{itime} # and finally prefer earlier charges
+ } @$results;
+ my @allocations;
+ my $rest_qty = $qty;
+
+ for my $chunk (@sorted_results) {
+ my $qty = min($chunk->{qty}, $rest_qty);
+
+ # since allocate operates on stock, this also ensures that no negative stock results are used
+ if ($qty > 0) {
+ push @allocations, SL::Helper::Inventory::Allocation->new(
+ parts_id => $chunk->{parts_id},
+ qty => $qty,
+ comment => $params{comment},
+ bin_id => $chunk->{bin_id},
+ warehouse_id => $chunk->{warehouse_id},
+ chargenumber => $chunk->{chargenumber},
+ bestbefore => $chunk->{bestbefore},
+ for_object_id => undef,
+ );
+ $rest_qty -= _round_number($qty, 5);
+ }
+ $rest_qty = _round_number($rest_qty, 5);
+ last if $rest_qty == 0;
+ }
+ if ($rest_qty > 0) {
+ die SL::X::Inventory::Allocation->new(
+ error => 'not enough to allocate',
+ msg => t8("can not allocate #1 units of #2, missing #3 units", _format_number($qty), $part->displayable_name, _format_number($rest_qty)),
+ );
+ } else {
+ if ($params{constraints}) {
+ check_constraints($params{constraints},\@allocations);
+ }
+ return @allocations;
+ }
+}
+
+sub allocate_for_assembly {
+ my (%params) = @_;
+
+ my $part = $params{part} or Carp::croak('allocate needs a part');
+ my $qty = $params{qty} or Carp::croak('allocate needs a qty');
+
+ Carp::croak('not an assembly') unless $part->is_assembly;
+
+ my %parts_to_allocate;
+
+ for my $assembly ($part->assemblies) {
+ $parts_to_allocate{ $assembly->part->id } //= 0;
+ $parts_to_allocate{ $assembly->part->id } += $assembly->qty * $qty;
+ }
+
+ my @allocations;
+
+ for my $part_id (keys %parts_to_allocate) {
+ my $part = SL::DB::Part->load_cached($part_id);
+ push @allocations, allocate(%params, part => $part, qty => $parts_to_allocate{$part_id});
+ }
+
+ @allocations;
+}
+
+sub check_constraints {
+ my ($constraints, $allocations) = @_;
+ if ('CODE' eq ref $constraints) {
+ if (!$constraints->(@$allocations)) {
+ die SL::X::Inventory::Allocation->new(
+ error => 'allocation constraints failure',
+ msg => t8("Allocations didn't pass constraints"),
+ );
+ }
+ } else {
+ croak 'constraints needs to be a hashref' unless 'HASH' eq ref $constraints;
+
+ my %supported_constraints = (
+ bin_id => 'bin_id',
+ warehouse_id => 'warehouse_id',
+ chargenumber => 'chargenumber',
+ );
+
+ for (keys %$constraints ) {
+ croak "unsupported constraint '$_'" unless $supported_constraints{$_};
+ next unless defined $constraints->{$_};
+
+ my %whitelist = map { (ref $_ ? $_->id : $_) => 1 } listify($constraints->{$_});
+ my $accessor = $supported_constraints{$_};
+
+ if (any { !$whitelist{$_->$accessor} } @$allocations) {
+ my %error_constraints = (
+ bin_id => t8('Bins'),
+ warehouse_id => t8('Warehouses'),
+ chargenumber => t8('Chargenumbers'),
+ );
+ my @allocs = grep { $whitelist{$_->$accessor} } @$allocations;
+ my $needed = sum map { $_->qty } grep { !$whitelist{$_->$accessor} } @$allocations;
+ my $err = t8("Cannot allocate parts.");
+ $err .= ' '.t8('part \'#\'1 in bin \'#2\' only with qty #3 (need additional #4) and chargenumber \'#5\'.',
+ SL::DB::Part->load_cached($_->parts_id)->description,
+ SL::DB::Bin->load_cached($_->bin_id)->full_description,
+ _format_number($_->qty), _format_number($needed), $_->chargenumber ? $_->chargenumber : '--') for @allocs;
+ die SL::X::Inventory::Allocation->new(
+ error => 'allocation constraints failure',
+ msg => $err,
+ );
+ }
+ }
+ }
+}
+
+sub produce_assembly {
+ my (%params) = @_;
+
+ my $part = $params{part} or Carp::croak('produce_assembly needs a part');
+ my $qty = $params{qty} or Carp::croak('produce_assembly needs a qty');
+
+ my $allocations = $params{allocations};
+ if ($params{auto_allocate}) {
+ Carp::croak("produce_assembly: can't have both allocations and auto_allocate") if $params{allocations};
+ $allocations = [ allocate_for_assembly(part => $part, qty => $qty) ];
+ } else {
+ Carp::croak("produce_assembly: need allocations or auto_allocate to produce something") if !$params{allocations};
+ $allocations = $params{allocations};
+ }
+
+ my $bin = $params{bin} or Carp::croak("need target bin");
+ my $chargenumber = $params{chargenumber};
+ my $bestbefore = $params{bestbefore};
+ my $for_object_id = $params{for_object_id};
+ my $comment = $params{comment} // '';
+
+ my $invoice = $params{invoice};
+ my $project = $params{project};
+
+ my $shippingdate = $params{shippingsdate} // DateTime->now_local;
+
+ my $trans_id = $params{trans_id};
+ ($trans_id) = selectrow_query($::form, SL::DB->client->dbh, qq|SELECT nextval('id')| ) unless $trans_id;
+
+ my $trans_type_out = SL::DB::Manager::TransferType->find_by(direction => 'out', description => 'used');
+ my $trans_type_in = SL::DB::Manager::TransferType->find_by(direction => 'in', description => 'assembled');
+
+ # check whether allocations are sane
+ if (!$params{no_check_allocations} && !$params{auto_allocate}) {
+ my %allocations_by_part = map { $_->parts_id => $_->qty } @$allocations;
+ for my $assembly ($part->assemblies) {
+ $allocations_by_part{ $assembly->parts_id } -= $assembly->qty * $qty;
+ }
+
+ die "allocations are insufficient for production" if any { $_ < 0 } values %allocations_by_part;
+ }
+
+ my @transfers;
+ for my $allocation (@$allocations) {
+ my $oe_id = delete $allocation->{for_object_id};
+ push @transfers, $allocation->transfer_object(
+ trans_id => $trans_id,
+ qty => -$allocation->qty,
+ trans_type => $trans_type_out,
+ shippingdate => $shippingdate,
+ employee => SL::DB::Manager::Employee->current,
+ );
+ }
+
+ push @transfers, SL::DB::Inventory->new(
+ trans_id => $trans_id,
+ trans_type => $trans_type_in,
+ part => $part,
+ qty => $qty,
+ bin => $bin,
+ warehouse => $bin->warehouse_id,
+ chargenumber => $chargenumber,
+ bestbefore => $bestbefore,
+ shippingdate => $shippingdate,
+ project => $project,
+ invoice => $invoice,
+ comment => $comment,
+ employee => SL::DB::Manager::Employee->current,
+ oe_id => $for_object_id,
+ );
+
+ SL::DB->client->with_transaction(sub {
+ $_->save for @transfers;
+ 1;
+ }) or do {
+ die SL::DB->client->error;
+ };
+
+ @transfers;
+}
+
+sub default_show_bestbefore {
+ $::instance_conf->get_show_bestbefore
+}
+
+1;
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::WH - Warehouse and Inventory API
+
+=head1 SYNOPSIS
+
+ # See description for an intro to the concepts used here.
+
+ use SL::Helper::Inventory qw(:ALL);
+
+ # stock, get "what's there" for a part with various conditions:
+ my $qty = get_stock(part => $part); # how much is on stock?
+ my $qty = get_stock(part => $part, date => $date); # how much was on stock at a specific time?
+ my $qty = get_stock(part => $part, bin => $bin); # how much is on stock in a specific bin?
+ my $qty = get_stock(part => $part, warehouse => $warehouse); # how much is on stock in a specific warehouse?
+ my $qty = get_stock(part => $part, chargenumber => $chargenumber); # how much is on stock of a specific chargenumber?
+
+ # onhand, get "what's available" for a part with various conditions:
+ my $qty = get_onhand(part => $part); # how much is available?
+ my $qty = get_onhand(part => $part, date => $date); # how much was available at a specific time?
+ my $qty = get_onhand(part => $part, bin => $bin); # how much is available in a specific bin?
+ my $qty = get_onhand(part => $part, warehouse => $warehouse); # how much is available in a specific warehouse?
+ my $qty = get_onhand(part => $part, chargenumber => $chargenumber); # how much is availbale of a specific chargenumber?
+
+ # onhand batch mode:
+ my $data = get_onhand(
+ warehouse => $warehouse,
+ by => [ qw(bin part chargenumber) ],
+ with_objects => [ qw(bin part) ],
+ );
+
+ # allocate:
+ my @allocations = allocate(
+ part => $part, # part_id works too
+ qty => $qty, # must be positive
+ chargenumber => $chargenumber, # optional, may be arrayref. if provided these charges will be used first
+ bestbefore => $datetime, # optional, defaults to today. items with bestbefore prior to that date wont be used
+ bin => $bin, # optional, may be arrayref. if provided
+ );
+
+ # shortcut to allocate all that is needed for producing an assembly, will use chargenumbers as appropriate
+ my @allocations = allocate_for_assembly(
+ part => $assembly, # part_id works too
+ qty => $qty, # must be positive
+ );
+
+ # create allocation manually, bypassing checks. all of these need to be passed, even undefs
+ my $allocation = SL::Helper::Inventory::Allocation->new(
+ part_id => $part->id,
+ qty => 15,
+ bin_id => $bin_obj->id,
+ warehouse_id => $bin_obj->warehouse_id,
+ chargenumber => '1823772365',
+ bestbefore => undef,
+ for_object_id => $order->id,
+ );
+
+ # produce_assembly:
+ produce_assembly(
+ part => $part, # target assembly
+ qty => $qty, # qty
+ allocations => \@allocations, # allocations to use. alternatively use "auto_allocate => 1,"
+
+ # where to put it
+ bin => $bin, # needed unless a global standard target is configured
+ chargenumber => $chargenumber, # optional
+ bestbefore => $datetime, # optional
+ comment => $comment, # optional
+ );
+
+=head1 DESCRIPTION
+
+New functions for the warehouse and inventory api.
+
+The WH api currently has three large shortcomings: It is very hard to just get
+the current stock for an item, it's extremely complicated to use it to produce
+assemblies while ensuring that no stock ends up negative, and it's very hard to
+use it to get an overview over the actual contents of the inventory.
+
+The first problem has spawned several dozen small functions in the program that
+try to implement that, and those usually miss some details. They may ignore
+bestbefore times, comments, ignore negative quantities etc.
+
+To get this cleaned up a bit this code introduces two concepts: stock and onhand.
+
+=over 4
+
+=item * Stock is defined as the actual contents of the inventory, everything that is
+there.
+
+=item * Onhand is what is available, which means things that are stocked,
+not expired and not in any other way reserved for other uses.
+
+=back
+
+The two new functions C<get_stock> and C<get_onhand> encapsulate these principles and
+allow simple access with some optional filters for chargenumbers or warehouses.
+Both of them have a batch mode that can be used to get these information to
+supplement simple reports.
+
+To address the safe assembly creation a new function has been added.
+C<allocate> will try to find the requested quantity of a part in the inventory
+and will return allocations of it which can then be used to create the
+assembly. Allocation will happen with the C<onhand> semantics defined above,
+meaning that by default no expired goods will be used. The caller can supply
+hints of what shold be used and in those cases chargenumbers will be used up as
+much as possible first. C<allocate> will always try to fulfil the request even
+beyond those. Should the required amount not be stocked, allocate will throw an
+exception.
+
+C<produce_assembly> has been rewritten to only accept parameters about the
+target of the production, and requires allocations to complete the request. The
+allocations can be supplied manually, or can be generated automatically.
+C<produce_assembly> will check whether enough allocations are given to create
+the assembly, but will not check whether the allocations are backed. If the
+allocations are not sufficient or if the auto-allocation fails an exception
+is returned. If you need to produce something that is not in the inventory, you
+can bypass those checks by creating the allocations yourself (see
+L</"ALLOCATION DATA STRUCTURE">).
+
+Note: this is only intended to cover the scenarios described above. For other cases:
+
+=over 4
+
+=item *
+
+If you need actual inventory objects because of record links or something like
+that load them directly. And strongly consider redesigning that, because it's
+really fragile.
+
+=item *
+
+You need weight or accounting information you're on your own. The inventory api
+only concerns itself with the raw quantities.
+
+=item *
+
+If you need the first stock date of parts, or anything related to a specific
+transfer type or direction, this is not covered yet.
+
+=back
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item * get_stock PARAMS
+
+Returns for single parts how much actually exists in the inventory.
+
+Options:
+
+=over 4
+
+=item * part
+
+The part. Must be present without C<by>. May be arrayref with C<by>. Can be object or id.
+
+=item * bin
+
+If given, will only return stock on these bins. Optional. May be array, May be object or id.
+
+=item * warehouse
+
+If given, will only return stock on these warehouses. Optional. May be array, May be object or id.
+
+=item * date
+
+If given, will return stock as it were on this timestamp. Optional. Must be L<DateTime> object.
+
+=item * chargenumber
+
+If given, will only show stock with this chargenumber. Optional. May be array.
+
+=item * by
+
+See L</"STOCK/ONHAND REPORT MODE">
+
+=item * with_objects
+
+See L</"STOCK/ONHAND REPORT MODE">
+
+=back
+
+Will return a single qty normally, see L</"STOCK/ONHAND REPORT MODE"> for batch
+mode when C<by> is given.
+
+=item * get_onhand PARAMS
+
+Returns for single parts how much is available in the inventory. That excludes
+stock with expired bestbefore.
+
+It takes the same options as L</get_stock>.
+
+=over 4
+
+=item * bestbefore
+
+If given, will only return stock with a bestbefore at or after the given date.
+Optional. Must be L<DateTime> object.
+
+=back
+
+=item * allocate PARAMS
+
+Accepted parameters:
+
+=over 4
+
+=item * part
+
+=item * qty
+
+=item * bin
+
+Bin object. Optional.
+
+=item * warehouse
+
+Warehouse object. Optional.
+
+=item * chargenumber
+
+Optional.
+
+=item * bestbefore
+
+Datetime. Optional.
+
+=back
+
+Tries to allocate the required quantity using what is currently onhand. If
+given any of C<bin>, C<warehouse>, C<chargenumber>
+
+=item * allocate_for_assembly PARAMS
+
+Shortcut to allocate everything for an assembly. Takes the same arguments. Will
+compute the required amount for each assembly part and allocate all of them.
+
+=item * produce_assembly
+
+
+=back
+
+=head1 STOCK/ONHAND REPORT MODE
+
+If the special option C<by> is given with an arrayref, the result will instead
+be an arrayref of partitioned stocks by those fields. Valid partitions are:
+
+=over 4
+
+=item * part
+
+If this is given, part is optional in the parameters
+
+=item * bin
+
+=item * warehouse
+
+=item * chargenumber
+
+=item * bestbefore
+
+=back
+
+Note: If you want to use the returned data to create allocations you I<need> to
+enable all of these. To make this easier a special shortcut exists
+
+In this mode, C<with_objects> can be used to load C<warehouse>, C<bin>,
+C<parts> objects in one go, just like with Rose. They
+need to be present in C<by> before that though.
+
+=head1 ALLOCATION ALGORITHM
+
+When calling allocate, the current onhand (== available stock) of the item will
+be used to decide which bins/chargenumbers/bestbefore can be used.
+
+In general allocate will try to make the request happen, and will use the
+provided charges up first, and then tap everything else. If you need to only
+I<exactly> use the provided charges, you'll need to craft the allocations
+yourself. See L</"ALLOCATION DATA STRUCTURE"> for that.
+
+If C<chargenumber> is given, those will be used up next.
+
+After that normal quantities will be used.
+
+These are tiebreakers and expected to rarely matter in reality. If you need
+finegrained control over which allocation is used, you may want to get the
+onhands yourself and select the appropriate ones.
+
+Only quantities with C<bestbefore> unset or after the given date will be
+considered. If more than one charge is eligible, the earlier C<bestbefore>
+will be used.
+
+Allocations do NOT have an internal memory and can't react to other allocations
+of the same part earlier. Never double allocate the same part within a
+transaction.
+
+=head1 ALLOCATION DATA STRUCTURE
+
+Allocations are instances of the helper class C<SL::Helper::Inventory::Allocation>. They require
+each of the following attributes to be set at creation time:
+
+=over 4
+
+=item * parts_id
+
+=item * qty
+
+=item * bin_id
+
+=item * warehouse_id
+
+=item * chargenumber
+
+=item * bestbefore
+
+=item * for_object_id
+
+If set the allocations will be marked as allocated for the given object.
+If these allocations are later used to produce an assembly, the resulting
+consuming transactions will be marked as belonging to the given object.
+The object may be an order, productionorder or other objects
+
+=back
+
+C<chargenumber>, C<bestbefore> and C<for_object_id> and C<comment> may be
+C<undef> (but must still be present at creation time). Instances are considered
+immutable.
+
+Allocations also provide the method C<transfer_object> which will create a new
+C<SL::DB::Inventory> bject with all the playload.
+
+=head1 CONSTRAINTS
+
+ # whitelist constraints
+ ->allocate(
+ ...
+ constraints => {
+ bin_id => \@allowed_bins,
+ chargenumber => \@allowed_chargenumbers,
+ }
+ );
+
+ # custom constraints
+ ->allocate(
+ constraints => sub {
+ # only allow chargenumbers with specific format
+ all { $_->chargenumber =~ /^ C \d{8} - \a{d2} $/x } @_
+
+ &&
+ # and must all have a bestbefore date
+ all { $_->bestbefore } @_;
+ }
+ )
+
+C<allocation> is "best effort" in nature. It will take the C<bin>,
+C<chargenumber> etc hints from the parameters, but will try it's bvest to
+fulfil the request anyway and only bail out if it is absolutely not possible.
+
+Sometimes you need to restrict allocations though. For this you can pass
+additional constraints to C<allocate>. A constraint serves as a whitelist.
+Every allocation must fulfil every constraint by having that attribute be one
+of the given values.
+
+In case even that is not enough, you may supply a custom check by passing a
+function that will be given the allocation objects.
+
+Note that both whitelists and constraints do not influence the order of
+allocations, which is done purely from the initial parameters. They only serve
+to reject allocations made in good faith which do fulfil required assertions.
+
+=head1 ERROR HANDLING
+
+C<allocate> and C<produce_assembly> will throw exceptions if the request can
+not be completed. The usual reason will be insufficient onhand to allocate, or
+insufficient allocations to process the request.
+
+=head1 KNOWN PROBLEMS
+
+ * It's not currently possible to identify allocations between requests, for
+ example for presenting the user possible allocations and then actually using
+ them on the next request.
+ * It's not currently possible to give C<allocate> prior constraints.
+ Currently all constraints are treated as hints (and will be preferred) but
+ the internal ordering of the hints is fixed and more complex preferentials
+ are not supported.
+ * bestbefore handling is untested
+ * interaction with config option "transfer_default_ignore_onhand" is
+ currently undefined (and implicitly ignores it)
+
+=head1 TODO
+
+ * define and describe error classes
+ * define wrapper classes for stock/onhand batch mode return values
+ * handle extra arguments in produce: shippingdate, project
+ * document no_ check
+ * tests
+
+=head1 BUGS
+
+None yet :)
+
+=head1 AUTHOR
+
+Sven Schöling E<lt>sven.schoeling@googlemail.comE<gt>
+
+=cut
--- /dev/null
+package SL::Helper::Inventory::Allocation;
+
+use strict;
+
+my @attributes = qw(parts_id qty bin_id warehouse_id chargenumber bestbefore comment for_object_id);
+my %attributes = map { $_ => 1 } @attributes;
+my %mapped_attributes = (
+ for_object_id => 'oe_id',
+);
+
+for my $name (@attributes) {
+ no strict 'refs';
+ *{"SL::Helper::Inventory::Allocation::$name"} = sub { $_[0]{$name} };
+}
+
+sub new {
+ my ($class, %params) = @_;
+
+ Carp::croak("missing attribute $_") for grep { !exists $params{$_} } @attributes;
+ Carp::croak("unknown attribute $_") for grep { !exists $attributes{$_} } keys %params;
+ Carp::croak("$_ must be set") for grep { !$params{$_} } qw(parts_id qty bin_id);
+ Carp::croak("$_ must be positive") for grep { !($params{$_} > 0) } qw(parts_id qty bin_id);
+
+ bless { %params }, $class;
+}
+
+sub transfer_object {
+ my ($self, %params) = @_;
+
+ SL::DB::Inventory->new(
+ (map {
+ my $attr = $mapped_attributes{$_} // $_;
+ $attr => $self->{$attr}
+ } @attributes),
+ %params,
+ );
+}
+
+1;
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Helper::Inventory::Allocation - Inventory API allocation data structure
+
+=head1 SYNOPSIS
+
+ # all of these need to be present
+ my $allocation = SL::Helper::Inventory::Allocation->new(
+ part_id => $part->id,
+ qty => 15,
+ bin_id => $bin_obj->id,
+ warehouse_id => $bin_obj->warehouse_id,
+ chargenumber => '1823772365', # can be undef
+ bestbefore => undef, # can be undef
+ for_object_id => $order->id, # can be undef
+ );
+
+
+=head1 SEE ALSO
+
+The full documentation can be found in L<SL::Helper::Inventory>
+
+=cut
use constant STDERR_TARGET => 1;
use Data::Dumper;
+use List::MoreUtils qw(all);
use POSIX qw(strftime getpid);
-use Scalar::Util qw(blessed refaddr weaken);
+use Scalar::Util qw(blessed refaddr weaken looks_like_number);
use Time::HiRes qw(gettimeofday tv_interval);
use SL::Request ();
use SL::YAML;
map { $column_lengths{$_} = length $row->{$_} if (length $row->{$_} > $column_lengths{$_}) } keys %{ $row };
}
+ my %alignment;
+ foreach my $column (keys %column_lengths) {
+ my $all_look_like_number = all { (($_->{$column} // '') eq '') || looks_like_number($_->{$column}) } @{ $results };
+ $alignment{$column} = $all_look_like_number ? '' : '-';
+ }
+
my @sorted_names = sort keys %column_lengths;
- my $format = join '|', map { '%' . $column_lengths{$_} . 's' } @sorted_names;
+ my $format = join '|', map { '%' . $alignment{$_} . $column_lengths{$_} . 's' } @sorted_names;
$prefix .= ' ' if $prefix;
use strict;
use parent qw(Rose::Object);
+use File::Slurp qw(read_file);
use List::MoreUtils qw(uniq);
use Time::HiRes qw();
use SL::Menu;
use SL::Presenter;
+use SL::System::Process;
my %menu_cache;
}
sub init_auto_reload_resources_param {
- return '' unless $::lx_office_conf{debug}->{auto_reload_resources};
- return sprintf('?rand=%d-%d-%d', Time::HiRes::gettimeofday(), int(rand 1000000000000));
+ return sprintf('?rand=%d-%d-%d', Time::HiRes::gettimeofday(), int(rand 1000000000000)) if $::lx_office_conf{debug}->{auto_reload_resources};
+
+ my $git_dir = SL::System::Process::exe_dir() . '/.git';
+
+ return '' unless -d $git_dir;
+
+ my $content = eval { scalar(read_file($git_dir . '/HEAD')) };
+
+ return '' unless ($content // '') =~ m{\Aref: ([^\r\n]+)};
+
+ $content = eval { scalar(read_file($git_dir . '/' . $1)) };
+
+ return '' unless ($content // '') =~ m{\A([0-9a-fA-F]+)};
+
+ return '?rand=' . $1;
}
##########################################
_set_id_attribute(\%attributes, $name);
+ $collection = [] if defined($collection) && !ref($collection) && ($collection eq '');
+
+ my $with_filter = delete($attributes{with_filter});
+ my $fil_placeholder = delete($attributes{filter_placeholder});
my $value_key = delete($attributes{value_key}) || 'id';
my $title_key = delete($attributes{title_key}) || $value_key;
my $default_key = delete($attributes{default_key}) || 'selected';
} @{ $collection };
}
- html_tag('select', $code, %attributes, name => $name);
+ my $select_html = html_tag('select', $code, %attributes, name => $name);
+
+ if ($with_filter) {
+ my $input_style;
+
+ if (($attributes{style} // '') =~ m{width: *(\d+) *px}i) {
+ $input_style = "width: " . ($1 - 22) . "px";
+ }
+
+ my $input_html = html_tag(
+ 'input', undef,
+ autocomplete => 'off',
+ type => 'text',
+ id => $attributes{id} . '_filter',
+ 'data-select-id' => $attributes{id},
+ (placeholder => $fil_placeholder) x !!$fil_placeholder,
+ (style => $input_style) x !!$input_style,
+ );
+ $select_html = html_tag('div', $input_html . $select_html, class => "filtered_select");
+ }
+
+ return $select_html;
}
sub checkbox_tag {
$::request->layout->add_javascripts('kivi.Validator.js');
$::request->presenter->need_reinit_widgets($params{id});
+ $params{'data-validate'} = join(' ', "date", grep { $_ } (delete $params{'data-validate'}));
+
input_tag(
$name, blessed($value) ? $value->to_lxoffice : $value,
size => 11,
- "data-validate" => "date",
%params,
%class, @onchange,
);
'SL::X::ZUGFeRDValidation' => {
isa => 'SL::X::Base',
},
+ 'SL::X::Inventory' => {
+ isa => 'SL::X::Base',
+ fields => [ qw(msg error) ],
+ defaults => { error_template => [ '%s: %s', qw(msg) ] },
+ },
+ 'SL::X::Inventory::Allocation' => {
+ isa => 'SL::X::Base',
+ fields => [ qw(msg error) ],
+ defaults => { error_template => [ '%s: %s', qw(msg) ] },
+ },
);
1;
my $report = SL::ReportGenerator->new(\%myconfig, $form);
@columns =
- qw(ids transdate id type invnumber ordnumber cusordnumber name netamount tax amount paid
+ qw(ids transdate id type invnumber ordnumber cusordnumber donumber deliverydate name netamount tax amount paid
datepaid due duedate transaction_description notes salesman employee shippingpoint shipvia
marge_total marge_percent globalprojectnumber customernumber country ustid taxzone
payment_terms charts customertype direct_debit dunning_description department);
'invnumber' => { 'text' => $locale->text('Invoice'), },
'ordnumber' => { 'text' => $locale->text('Order'), },
'cusordnumber' => { 'text' => $locale->text('Customer Order Number'), },
+ 'donumber' => { 'text' => $locale->text('Delivery Order'), },
+ 'deliverydate' => { 'text' => $locale->text('Delivery Date'), },
'name' => { 'text' => $locale->text('Customer'), },
'netamount' => { 'text' => $locale->text('Amount'), },
'tax' => { 'text' => $locale->text('Tax'), },
%column_defs_cvars,
);
- foreach my $name (qw(id transdate duedate invnumber ordnumber cusordnumber name datepaid employee shippingpoint shipvia transaction_description direct_debit department)) {
+ foreach my $name (qw(id transdate duedate invnumber ordnumber cusordnumber donumber deliverydate name datepaid employee shippingpoint shipvia transaction_description direct_debit department)) {
my $sortdir = $form->{sort} eq $name ? 1 - $form->{sortdir} : $form->{sortdir};
$column_defs{$name}->{link} = $href . "&sort=$name&sortdir=$sortdir";
}
$form->{totaldebit} = 0;
$form->{totalcredit} = 0;
- my %project_labels = ();
- my @project_values = ("");
- foreach my $item (@{ $form->{"ALL_PROJECTS"} }) {
- push(@project_values, $item->{"id"});
- $project_labels{$item->{"id"}} = $item->{"projectnumber"};
- }
-
my %charts_by_id = map { ($_->{id} => $_) } @{ $::form->{ALL_CHARTS} };
my $default_chart = $::form->{ALL_CHARTS}[0];
my $transdate = $::form->{transdate} ? DateTime->from_kivitendo($::form->{transdate}) : DateTime->today_local;
}
}
- my $projectnumber =
- NTI($cgi->popup_menu('-name' => "project_id_$i",
- '-values' => \@project_values,
- '-labels' => \%project_labels,
- '-default' => $form->{"project_id_$i"} ));
- my $projectnumber_hidden = qq|
- <input type="hidden" name="project_id_$i" value="$form->{"project_id_$i"}">|;
+ my $projectnumber = SL::Presenter::Project::picker("project_id_$i", $form->{"project_id_$i"});
+ my $projectnumber_hidden = SL::Presenter::Tag::hidden_tag("project_id_$i", $form->{"project_id_$i"});
- my $balance = $form->format_amount(\%::myconfig, $balances{$accno_id} // 0, 2, 'DRCR');
+ my $copy2credit = $i == 1 ? 'onkeyup="copy_debit_to_credit()"' : '';
+ my $balance = $form->format_amount(\%::myconfig, $balances{$accno_id} // 0, 2, 'DRCR');
# if we have a bt_chart_id we disallow changing the amount of the bank account
if ($form->{bt_chart_id}) {
$debitreadonly = $creditreadonly = "readonly" if ($form->{"accno_id_$i"} eq $form->{bt_chart_id});
+ $copy2credit = '' if $i == 1; # and disallow copy2credit
}
print qq|<tr valign=top>
$accno
<td id="chart_balance_$i" align="right">${balance}</td>
$fx_transaction
- <td><input name="debit_$i" size="8" value="$form->{"debit_$i"}" accesskey=$i $debitreadonly></td>
+ <td><input name="debit_$i" size="8" value="$form->{"debit_$i"}" accesskey=$i $copy2credit $debitreadonly></td>
<td><input name="credit_$i" size=8 value="$form->{"credit_$i"}" $creditreadonly></td>
<td><input type="hidden" name="tax_$i" value="$form->{"tax_$i"}">$form->{"tax_$i"}</td>
$tax_ddbox|;
my ($init) = @_;
- $::request->layout->add_javascripts("autocomplete_chart.js", "kivi.File.js", "kivi.GL.js", "kivi.RecordTemplate.js");
-
- my @old_project_ids = grep { $_ } map{ $::form->{"project_id_$_"} } 1..$::form->{rowcount};
+ $::request->layout->add_javascripts("autocomplete_chart.js", "autocomplete_project.js", "kivi.File.js", "kivi.GL.js", "kivi.RecordTemplate.js");
- $::form->get_lists("projects" => { "key" => "ALL_PROJECTS",
- "all" => 0,
- "old_id" => \@old_project_ids },
+ my @old_project_ids = grep { $_ } map{ $::form->{"project_id_$_"} } 1..$::form->{rowcount};
+ my @conditions = @old_project_ids ? (id => \@old_project_ids) : ();
+ $::form->{ALL_PROJECTS} = SL::DB::Manager::Project->get_all_sorted(query => [ or => [ active => 1, @conditions ]]);
- "charts" => { "key" => "ALL_CHARTS",
- "transdate" => $::form->{transdate} });
+ $::form->get_lists(
+ "charts" => { "key" => "ALL_CHARTS", "transdate" => $::form->{transdate} },
+ );
# we cannot book on charttype header
@{ $::form->{ALL_CHARTS} } = grep { $_->{charttype} ne 'H' } @{ $::form->{ALL_CHARTS} };
$ship_qty /= ( $all_units->{$form->{"unit_$i"}}->{factor} || 1 );
$column_data{ship} = $form->format_amount(\%myconfig, $form->round_amount($ship_qty, 2) * 1) . ' ' . $form->{"unit_$i"}
- . $cgi->hidden(-name => "ship_$i", -value => $form->format_amount(\%myconfig, $form->{"ship_$i"}, $qty_dec));
+ . $cgi->hidden(-name => "ship_$i", -value => $form->{"ship_$i"}, $qty_dec);
my $ship_missing_qty = $form->{"qty_$i"} - $ship_qty;
my $ship_missing_amount = $form->round_amount($ship_missing_qty * $form->{"sellprice_$i"} * (100 - $form->{"discount_$i"}) / 100 / $price_factor, 2);
$main::lxdebug->leave_sub();
}
-sub oe_delivery_order_from_order {
-
+sub oe_prepare_xyz_from_order {
return if !$::form->{id};
my $order = SL::DB::Order->new(id => $::form->{id})->load;
$::form->{rowcount}++;
_update_ship();
+}
+
+sub oe_delivery_order_from_order {
+ oe_prepare_xyz_from_order();
delivery_order();
}
sub oe_invoice_from_order {
-
- return if !$::form->{id};
-
- my $order = SL::DB::Order->new(id => $::form->{id})->load;
- $order->flatten_to_form($::form, format_amounts => 1);
-
- # hack: add partsgroup for first row if it does not exists,
- # because _remove_billed_or_delivered_rows and _remove_full_delivered_rows
- # determine fields to handled by existing fields for the first row. If partsgroup
- # is missing there, for deleted rows the partsgroup_field is not emptied and in
- # update_delivery_order it will not considered an empty row ...
- $::form->{partsgroup_1} = '' if !exists $::form->{partsgroup_1};
-
- # fake last empty row
- $::form->{rowcount}++;
-
- _update_ship();
+ oe_prepare_xyz_from_order();
invoice();
}
};
}
# for older configs, replace email preset text if not yet set.
- $config->{email_subject} ||= GenericTranslations->get(language_id => $::form->{lanuage_id},
- translation_type =>"preset_text_periodic_invoices_email_subject");
- $config->{email_body} ||= GenericTranslations->get(language_id => $::form->{lanuage_id},
- translation_type =>"preset_text_periodic_invoices_email_body");
+ $config->{email_subject} ||= GenericTranslations->get(language_id => $::form->{lanuage_id}, translation_type => "preset_text_periodic_invoices_email_subject");
+ $config->{email_body} ||= GenericTranslations->get(language_id => $::form->{lanuage_id}, translation_type => "salutation_general")
+ . GenericTranslations->get(language_id => $::form->{lanuage_id}, translation_type => "salutation_punctuation_mark")
+ . "\n\n"
+ . GenericTranslations->get(language_id => $::form->{lanuage_id}, translation_type => "preset_text_periodic_invoices_email_body");
+ $config->{email_body} =~ s{\A[ \n\r]+|[ \n\r]+\Z}{}g;
$config->{periodicity} = 'm' if none { $_ eq $config->{periodicity} } @SL::DB::PeriodicInvoicesConfig::PERIODICITIES;
$config->{order_value_periodicity} = 'p' if none { $_ eq $config->{order_value_periodicity} } ('p', @SL::DB::PeriodicInvoicesConfig::ORDER_VALUE_PERIODICITIES);
next unless $::form->{"id_$row"};
my $base_factor = SL::DB::Manager::Unit->find_by(name => $::form->{"unit_$row"})->base_factor;
my $base_qty = $::form->parse_amount(\%::myconfig, $::form->{"qty_$row"}) * $base_factor;
- my $ship_qty = $::form->parse_amount(\%::myconfig, $::form->{"ship_$row"}) * $base_factor;
+ my $ship_qty = $::form->{"ship_$row"} * $base_factor;
#$main::lxdebug->message(LXDebug->DEBUG2(),"shipto=".$ship_qty." qty=".$base_qty);
if (!$ship_qty || ($ship_qty < $base_qty)) {
accesskey => 'enter',
tooltip => t8('Post payments for selected invoices'),
checks => [ [ 'kivi.check_if_entries_selected', '[name="ids[]"]' ] ],
- only_if => $params{show_post_payments_button},
+ disabled => $params{show_post_payments_button} ? undef : t8('All payments have already been posted.'),
],
action => [
t8('Payment list'),
accesskey => 'enter',
tooltip => t8('Download list of payments as PDF'),
checks => [ [ 'kivi.check_if_entries_selected', '[name="ids[]"]' ] ],
- not_if => $params{show_post_payments_button},
+ disabled => $params{show_post_payments_button} ? t8('All payments must be posted before the payment list can be downloaded.') : undef,
],
);
}
.cke_button {
padding: 0px; 6px !important;
}
+
+/* selects with text filters */
+div.filtered_select input, div.filtered_select select {
+ display: block;
+}
+
+div.filtered_select input {
+ background-image: url(../../image/glass14x14.png);
+ background-repeat: no-repeat;
+ background-position: 2px 2px;
+ border-radius: 0px;
+ border: solid #a0a0a0 1px;
+ border-bottom: none;
+ padding: 0px;
+ padding-left: 20px;
+ margin: 0;
+ width: 500px;
+}
+
+div.filtered_select select {
+ width: 522px;
+}
.cke_button {
padding: 0px; 6px !important;
}
+
+/* selects with text filters */
+div.filtered_select input, div.filtered_select select {
+ display: block;
+}
+
+div.filtered_select > input {
+ background-image: url(../../image/glass14x14.png);
+ background-repeat: no-repeat;
+ background-position: 2px 2px;
+ border-radius: 0px;
+ border: solid #a0a0a0 1px;
+ border-bottom: none;
+ padding: 0px;
+ padding-left: 20px;
+ margin: 0;
+ width: 500px;
+}
+
+div.filtered_select select {
+ width: 522px;
+}
Mittelgroße neue Features:
- - Der Import von Bankauszuügen im MT940-Format wurde komplett neu
+ - Der Import von Bankauszügen im MT940-Format wurde komplett neu
geschrieben. Das externe Programm AQBanking wird nun nicht mehr
benötigt.
Kleinere neue Features und Detailverbesserungen:
- - Neuer Order-Controller: Unterstütztung für Übersetzungen von
+ - Neuer Order-Controller: Unterstützung für Übersetzungen von
Artikeln wurde implementiert.
- Einkaufs-/Verkaufsbelege: die Belegsprache ist nun als Auswahl
direkt in der Hauptmaske vorhanden und nicht mehr in den
Bugfixes (Tracker: https://www.kivitendo.de/redmine):
97 Benutzer löschen unter System->Benutzer funktioniert nicht
+408 Neuer Auftragskontroller: Drucken von odt-Vorlagen geht nur mit Standardvorlage
2020-10-02 - Release 3.5.6.1
debianoiden Betriebssystemen installiert man die Pakete mit:</para>
<para><programlisting>apt install texlive-base-bin texlive-latex-recommended texlive-fonts-recommended \
- texlive-latex-extra texlive-lang-german texlive-generic-extra texlive-xetex ghostscript</programlisting></para>
+ texlive-latex-extra texlive-lang-german ghostscript</programlisting></para>
<para>Für Fedora benötigen Sie die folgenden Pakete:</para>
libimage-info-perl libgd-gd2-perl libapache2-mod-fcgid \
libfile-copy-recursive-perl postgresql libalgorithm-checkdigits-perl \
libcrypt-pbkdf2-perl git libcgi-pm-perl libtext-unidecode-perl libwww-perl\
- postgresql-contrib aqbanking-tools poppler-utils libhtml-restrict-perl\
+ postgresql-contrib poppler-utils libhtml-restrict-perl\
libdatetime-set-perl libset-infinite-perl liblist-utilsby-perl\
libdaemon-generic-perl libfile-flock-perl libfile-slurp-perl\
libfile-mimeinfo-perl libpbkdf2-tiny-perl libregexp-ipv6-perl \
</pre><p>Zusätzlich müssen einige Pakete aus dem CPAN installiert
werden. Dazu können Sie die folgenden Befehle anwenden:</p><pre class="programlisting">cpan DateTime::event::Cron DateTime::Set FCGI \
HTML::Restrict PBKDF2::Tiny Rose::Db::Object Set::Infinite</pre></div></div><div class="sect2" title="2.2.3. Andere Pakete installieren"><div class="titlepage"><div><div><h3 class="title"><a name="d0e649"></a>2.2.3. Andere Pakete installieren</h3></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
- <code class="literal">aqbanking-tools</code> Für das Parsen des MT940 Bankformats (Version 6 oder höher)</p></li><li class="listitem"><p>
<code class="literal">poppler-utils</code> 'pdfinfo' zum Erkennen der Seitenanzahl bei der PDF-Generierung</p></li><li class="listitem"><p>
- <code class="literal">Postgres Trigram-Index</code> Für datenbankoptimierte Suchanfragen. Bspw. im Paket <code class="literal">postgresql-contrib</code> enthalten</p></li></ul></div><p>Debian und Ubuntu: </p><pre class="programlisting">apt install aqbanking-tools postgresql-contrib poppler-utils</pre><p>
- </p><p>Fedora: </p><pre class="programlisting">dnf install aqbanking poppler-utils postgresql-contrib</pre><p>
- </p><p>openSUSE: </p><pre class="programlisting">zypper install aqbanking-tools poppler-tools</pre><p>
+ <code class="literal">Postgres Trigram-Index</code> Für datenbankoptimierte Suchanfragen. Bspw. im Paket <code class="literal">postgresql-contrib</code> enthalten</p></li></ul></div><p>Debian und Ubuntu: </p><pre class="programlisting">apt install postgresql-contrib poppler-utils</pre><p>
+ </p><p>Fedora: </p><pre class="programlisting">dnf install poppler-utils postgresql-contrib</pre><p>
+ </p><p>openSUSE: </p><pre class="programlisting">zypper install poppler-tools</pre><p>
</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch02.html">Zurück</a> </td><td width="20%" align="center"><a accesskey="u" href="ch02.html">Nach oben</a></td><td width="40%" align="right"> <a accesskey="n" href="ch02s03.html">Weiter</a></td></tr><tr><td width="40%" align="left" valign="top">Kapitel 2. Installation und Grundkonfiguration </td><td width="20%" align="center"><a accesskey="h" href="index.html">Zum Anfang</a></td><td width="40%" align="right" valign="top"> 2.3. Manuelle Installation des Programmpaketes</td></tr></table></div></body></html>
\ No newline at end of file
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>2.6. Webserver-Konfiguration</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="up" href="ch02.html" title="Kapitel 2. Installation und Grundkonfiguration"><link rel="prev" href="ch02s05.html" title="2.5. Anpassung der PostgreSQL-Konfiguration"><link rel="next" href="ch02s07.html" title="2.7. Der Task-Server"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">2.6. Webserver-Konfiguration</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch02s05.html">Zurück</a> </td><th width="60%" align="center">Kapitel 2. Installation und Grundkonfiguration</th><td width="20%" align="right"> <a accesskey="n" href="ch02s07.html">Weiter</a></td></tr></table><hr></div><div class="sect1" title="2.6. Webserver-Konfiguration"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="Apache-Konfiguration"></a>2.6. Webserver-Konfiguration</h2></div></div></div><div class="sect2" title="2.6.1. Grundkonfiguration mittels CGI"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1129"></a>2.6.1. Grundkonfiguration mittels CGI</h3></div></div></div><div class="note" title="Anmerkung" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Anmerkung]" src="system/docbook-xsl/images/note.png"></td><th align="left">Anmerkung</th></tr><tr><td align="left" valign="top"><p>Für einen deutlichen Performanceschub sorgt die Ausführung
+ <title>2.6. Webserver-Konfiguration</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="up" href="ch02.html" title="Kapitel 2. Installation und Grundkonfiguration"><link rel="prev" href="ch02s05.html" title="2.5. Anpassung der PostgreSQL-Konfiguration"><link rel="next" href="ch02s07.html" title="2.7. Der Task-Server"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">2.6. Webserver-Konfiguration</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch02s05.html">Zurück</a> </td><th width="60%" align="center">Kapitel 2. Installation und Grundkonfiguration</th><td width="20%" align="right"> <a accesskey="n" href="ch02s07.html">Weiter</a></td></tr></table><hr></div><div class="sect1" title="2.6. Webserver-Konfiguration"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="Apache-Konfiguration"></a>2.6. Webserver-Konfiguration</h2></div></div></div><div class="sect2" title="2.6.1. Grundkonfiguration mittels CGI"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1123"></a>2.6.1. Grundkonfiguration mittels CGI</h3></div></div></div><div class="note" title="Anmerkung" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Anmerkung]" src="system/docbook-xsl/images/note.png"></td><th align="left">Anmerkung</th></tr><tr><td align="left" valign="top"><p>Für einen deutlichen Performanceschub sorgt die Ausführung
mittels FastCGI/FCGI. Die Einrichtung wird ausführlich im Abschnitt
<a class="xref" href="ch02s06.html#Apache-Konfiguration.FCGI" title="2.6.2. Konfiguration für FastCGI/FCGI">Konfiguration für FastCGI/FCGI</a> beschrieben.</p></td></tr></table></div><p>Der Zugriff auf das Programmverzeichnis muss in der Apache
Webserverkonfigurationsdatei <code class="literal">httpd.conf</code> eingestellt
Alias /url/for/kivitendo-erp-fcgid/ /path/to/kivitendo-erp/</pre><p>Dann ist unter <code class="filename">/url/for/kivitendo-erp/</code>
die normale Version erreichbar, und unter
<code class="constant">/url/for/kivitendo-erp-fcgid/</code> die
- FastCGI-Version.</p></div></div><div class="sect2" title="2.6.3. Authentifizierung mittels HTTP Basic Authentication"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1283"></a>2.6.3. Authentifizierung mittels HTTP Basic Authentication</h3></div></div></div><p>
+ FastCGI-Version.</p></div></div><div class="sect2" title="2.6.3. Authentifizierung mittels HTTP Basic Authentication"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1277"></a>2.6.3. Authentifizierung mittels HTTP Basic Authentication</h3></div></div></div><p>
Kivitendo unterstützt, dass Benutzerauthentifizierung über den Webserver mittels des »Basic«-HTTP-Authentifizierungs-Schema erfolgt
(siehe <a class="ulink" href="https://tools.ietf.org/html/rfc7617" target="_top">RFC 7617</a>). Dazu ist es aber nötig, dass der dabei vom Client
mitgeschickte Header <code class="constant">Authorization</code> vom Webserver an Kivitendo über die Umgebungsvariable
<code class="constant">HTTP_AUTHORIZATION</code> weitergegeben wird, was standardmäßig nicht der Fall ist. Für Apache kann dies über die
folgende Konfigurationsoption aktiviert werden:
- </p><pre class="programlisting">SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1</pre></div><div class="sect2" title="2.6.4. Aktivierung von mod_rewrite/directory_match für git basierte Installationen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1299"></a>2.6.4. Aktivierung von mod_rewrite/directory_match für git basierte Installationen</h3></div></div></div><p>
+ </p><pre class="programlisting">SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1</pre></div><div class="sect2" title="2.6.4. Aktivierung von mod_rewrite/directory_match für git basierte Installationen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1293"></a>2.6.4. Aktivierung von mod_rewrite/directory_match für git basierte Installationen</h3></div></div></div><p>
Aufgrund von aktuellen (Mitte 2020) Sicherheitswarnungen für git basierte Webanwendungen ist die mitausgelieferte .htaccess
restriktiver geworden und verhindert somit das Auslesen von git basierten Daten.
Für debian/ubuntu muss das Modul mod_rewrite einmalig so aktiviert werden:
Require all denied
</DirectoryMatch></pre><p>
- </p></div><div class="sect2" title="2.6.5. Weitergehende Konfiguration"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1313"></a>2.6.5. Weitergehende Konfiguration</h3></div></div></div><p>Für einen deutlichen Sicherheitsmehrwert sorgt die Ausführung
+ </p></div><div class="sect2" title="2.6.5. Weitergehende Konfiguration"><div class="titlepage"><div><div><h3 class="title"><a name="d0e1307"></a>2.6.5. Weitergehende Konfiguration</h3></div></div></div><p>Für einen deutlichen Sicherheitsmehrwert sorgt die Ausführung
von kivitendo nur über https-verschlüsselten Verbindungen, sowie
weiteren Zusatzmassnahmen, wie beispielsweise Basic Authenticate. Die
Konfigurationsmöglichkeiten sprengen allerdings den Rahmen dieser
Anleitung, hier ein Hinweis auf einen entsprechenden <a class="ulink" href="http://redmine.kivitendo-premium.de/boards/1/topics/142" target="_top">Foreneintrag
(Stand Sept. 2015)</a> und einen aktuellen (Stand Mai 2017) <a class="ulink" href="https://mozilla.github.io/server-side-tls/ssl-config-generator/" target="_top">
- SSL-Konfigurations-Generator</a>.</p></div><div class="sect3" title="2.6.1. Aktivierung von Apache2 modsecurity"><div class="titlepage"><div><div><h4 class="title"><a name="d0e1324"></a>2.6.1. Aktivierung von Apache2 modsecurity</h4></div></div></div><p>Aufgrund des OpenSource Charakters ist kivitendo nicht "out of the box" sicher.
+ SSL-Konfigurations-Generator</a>.</p></div><div class="sect3" title="2.6.1. Aktivierung von Apache2 modsecurity"><div class="titlepage"><div><div><h4 class="title"><a name="d0e1318"></a>2.6.1. Aktivierung von Apache2 modsecurity</h4></div></div></div><p>Aufgrund des OpenSource Charakters ist kivitendo nicht "out of the box" sicher.
Organisatorisch empfehlen wir hier die enge Zusammenarbeit mit einem kivitendo Partner der auch in der
Lage ist weiterführende Fragen in Bezug auf Datenschutz und Datensicherheit zu beantworten.
Unabhängig davon empfehlen wir im Webserver Bereich die Aktivierung und Konfiguration des Moduls modsecurity für den Apache2, damit
Links aus einem der Runlevel-Verzeichnisse heraus in den Boot-Prozess
einzubinden. Da das bei neueren Linux-Distributionen aber nicht
zwangsläufig funktioniert, werden auch Start-Scripte mitgeliefert, die
- anstelle eines symbolischen Links verwendet werden können.</p><div class="sect3" title="2.7.3.1. SystemV-basierende Systeme (z.B. ältere Debian, ältere openSUSE, ältere Fedora)"><div class="titlepage"><div><div><h4 class="title"><a name="d0e1397"></a>2.7.3.1. SystemV-basierende Systeme (z.B. ältere Debian, ältere
+ anstelle eines symbolischen Links verwendet werden können.</p><div class="sect3" title="2.7.3.1. SystemV-basierende Systeme (z.B. ältere Debian, ältere openSUSE, ältere Fedora)"><div class="titlepage"><div><div><h4 class="title"><a name="d0e1391"></a>2.7.3.1. SystemV-basierende Systeme (z.B. ältere Debian, ältere
openSUSE, ältere Fedora)</h4></div></div></div><p>Kopieren Sie die Datei
<code class="filename">scripts/boot/system-v/kivitendo-task-server</code>
nach <code class="filename">/etc/init.d/kivitendo-task-server</code>. Passen
<code class="literal">DAEMON=....</code>). Binden Sie das Script in den
Boot-Prozess ein. Dies ist distributionsabhängig:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Debian-basierende Systeme:</p><pre class="programlisting">update-rc.d kivitendo-task-server defaults
insserv kivitendo-task-server</pre></li><li class="listitem"><p>Ältere openSUSE und ältere Fedora:</p><pre class="programlisting">chkconfig --add kivitendo-task-server</pre></li></ul></div><p>Danach kann der Task-Server mit dem folgenden Befehl gestartet
- werden:</p><pre class="programlisting">/etc/init.d/kivitendo-task-server start</pre></div><div class="sect3" title="2.7.3.2. Upstart-basierende Systeme (z.B. Ubuntu bis 14.04)"><div class="titlepage"><div><div><h4 class="title"><a name="d0e1426"></a>2.7.3.2. Upstart-basierende Systeme (z.B. Ubuntu bis 14.04)</h4></div></div></div><p>Kopieren Sie die Datei
+ werden:</p><pre class="programlisting">/etc/init.d/kivitendo-task-server start</pre></div><div class="sect3" title="2.7.3.2. Upstart-basierende Systeme (z.B. Ubuntu bis 14.04)"><div class="titlepage"><div><div><h4 class="title"><a name="d0e1420"></a>2.7.3.2. Upstart-basierende Systeme (z.B. Ubuntu bis 14.04)</h4></div></div></div><p>Kopieren Sie die Datei
<code class="filename">scripts/boot/upstart/kivitendo-task-server.conf</code>
nach <code class="filename">/etc/init/kivitendo-task-server.conf</code>.
Passen Sie in der kopierten Datei den Pfad zum Task-Server an (Zeile
<code class="literal">exec ....</code>).</p><p>Danach kann der Task-Server mit dem folgenden Befehl gestartet
- werden:</p><pre class="programlisting">service kivitendo-task-server start</pre></div><div class="sect3" title="2.7.3.3. systemd-basierende Systeme (z.B. neure openSUSE, neuere Fedora, neuere Ubuntu und neuere Debians)"><div class="titlepage"><div><div><h4 class="title"><a name="d0e1444"></a>2.7.3.3. systemd-basierende Systeme (z.B. neure openSUSE, neuere
+ werden:</p><pre class="programlisting">service kivitendo-task-server start</pre></div><div class="sect3" title="2.7.3.3. systemd-basierende Systeme (z.B. neure openSUSE, neuere Fedora, neuere Ubuntu und neuere Debians)"><div class="titlepage"><div><div><h4 class="title"><a name="d0e1438"></a>2.7.3.3. systemd-basierende Systeme (z.B. neure openSUSE, neuere
Fedora, neuere Ubuntu und neuere Debians)</h4></div></div></div><p>Kopieren Sie die Datei
<code class="filename">scripts/boot/systemd/kivitendo-task-server.service</code>
nach <code class="filename">/etc/systemd/system/</code>. Passen Sie in der
Verzeichnis umbenannt werden.</p><p>Dieses Verzeichnis, wie auch das komplette
<code class="literal">users</code>-Verzeichnis, muss vom Webserver beschreibbar
sein. Dieses wurde bereits erledigt (siehe <a class="xref" href="ch02s03.html" title="2.3. Manuelle Installation des Programmpaketes">Manuelle Installation des Programmpaketes</a>), kann aber erneut
- überprüft werden, wenn die Konvertierung nach PDF fehlschlägt.</p><div class="sect2" title="2.13.1. OpenDocument (odt) Druckvorlagen mit Makros"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2420"></a>2.13.1. OpenDocument (odt) Druckvorlagen mit Makros</h3></div></div></div><p>OpenDocument Vorlagen können Makros enthalten, welche komplexere
+ überprüft werden, wenn die Konvertierung nach PDF fehlschlägt.</p><div class="sect2" title="2.13.1. OpenDocument (odt) Druckvorlagen mit Makros"><div class="titlepage"><div><div><h3 class="title"><a name="d0e2414"></a>2.13.1. OpenDocument (odt) Druckvorlagen mit Makros</h3></div></div></div><p>OpenDocument Vorlagen können Makros enthalten, welche komplexere
Aufgaben erfüllen.</p><p>Der Vorlagensatz "rev-odt" enthält solche Vorlagen mit <span class="bold"><strong>Schweizer Bank-Einzahlungsscheinen (BESR)</strong></span>.
Diese Makros haben die Aufgabe, die in den Einzahlungsscheinen
benötigte Referenznummer und Kodierzeile zu erzeugen. Hier eine kurze
Beschreibung, wie die Makros aufgebaut sind, und was bei ihrer Nutzung
zu beachten ist (<span class="bold"><strong>in fett sind nötige einmalige
- Anpassungen aufgeführt</strong></span>):</p><div class="sect3" title="2.13.1.1. Bezeichnung der Vorlagen"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2433"></a>2.13.1.1. Bezeichnung der Vorlagen</h4></div></div></div><p>Rechnung: invoice_besr.odt, Auftrag:
- sales_order_besr.odt</p></div><div class="sect3" title="2.13.1.2. Vorbereitungen im Adminbereich"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2438"></a>2.13.1.2. Vorbereitungen im Adminbereich</h4></div></div></div><p>Damit beim Erstellen von Rechnungen und Aufträgen neben der
+ Anpassungen aufgeführt</strong></span>):</p><div class="sect3" title="2.13.1.1. Bezeichnung der Vorlagen"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2427"></a>2.13.1.1. Bezeichnung der Vorlagen</h4></div></div></div><p>Rechnung: invoice_besr.odt, Auftrag:
+ sales_order_besr.odt</p></div><div class="sect3" title="2.13.1.2. Vorbereitungen im Adminbereich"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2432"></a>2.13.1.2. Vorbereitungen im Adminbereich</h4></div></div></div><p>Damit beim Erstellen von Rechnungen und Aufträgen neben der
Standardvorlage ohne Einzahlungsschein weitere Vorlagen (z.B. mit
Einzahlungsschein) auswählbar sind, muss für jedes Vorlagen-Suffix
ein Drucker eingerichtet werden:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Druckeradministration → Drucker hinzufügen</p></li><li class="listitem"><p>Mandant wählen</p></li><li class="listitem"><p>Druckerbeschreibung → aussagekräftiger Text: wird in der
Aufträgen oder Rechnungen als odt-Datei keine Bedeutung, darf
aber nicht leer sein)</p></li><li class="listitem"><p>Vorlagenkürzel → besr bzw. selbst gewähltes Vorlagensuffix
(muss genau der Zeichenfolge entsprechen, die zwischen
- "invoice_" bzw. "sales_order_" und ".odt" steht.)</p></li><li class="listitem"><p>speichern</p></li></ul></div></div><div class="sect3" title="2.13.1.3. Benutzereinstellungen"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2462"></a>2.13.1.3. Benutzereinstellungen</h4></div></div></div><p>Wer den Ausdruck mit Einzahlungsschein als Standardeinstellung
+ "invoice_" bzw. "sales_order_" und ".odt" steht.)</p></li><li class="listitem"><p>speichern</p></li></ul></div></div><div class="sect3" title="2.13.1.3. Benutzereinstellungen"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2456"></a>2.13.1.3. Benutzereinstellungen</h4></div></div></div><p>Wer den Ausdruck mit Einzahlungsschein als Standardeinstellung
im Rechnungs- bzw. Auftragsformular angezeigt haben möchte, kann
dies persönlich für sich bei den Benutzereinstellungen
konfigurieren:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Programm → Benutzereinstellungen → Druckoptionen</p></li><li class="listitem"><p>Standardvorlagenformat → OpenDocument/OASIS</p></li><li class="listitem"><p>Standardausgabekanal → Bildschirm</p></li><li class="listitem"><p>Standarddrucker → gewünschte Druckerbeschreibung auswählen
- (z.B. mit Einzahlungsschein Bank xy)</p></li><li class="listitem"><p>Anzahl Kopien → leer</p></li><li class="listitem"><p>speichern</p></li></ul></div></div><div class="sect3" title="2.13.1.4. Aufbau und nötige Anpassungen der Vorlagen"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2486"></a>2.13.1.4. Aufbau und nötige Anpassungen der Vorlagen</h4></div></div></div><p>In der Vorlage sind als Modul "BESR" 4 Makros gespeichert, die
+ (z.B. mit Einzahlungsschein Bank xy)</p></li><li class="listitem"><p>Anzahl Kopien → leer</p></li><li class="listitem"><p>speichern</p></li></ul></div></div><div class="sect3" title="2.13.1.4. Aufbau und nötige Anpassungen der Vorlagen"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2480"></a>2.13.1.4. Aufbau und nötige Anpassungen der Vorlagen</h4></div></div></div><p>In der Vorlage sind als Modul "BESR" 4 Makros gespeichert, die
aus dem von kivitendo erzeugten odt-Dokument die korrekte
Referenznummer inklusive Prüfziffer sowie die Kodierzeile in
OCRB-Schrift erzeugen und am richtigen Ort ins Dokument
angepasst werden.</strong></span> Dabei ist darauf zu achten, dass
sich die Positionen der Postkonto-Nummern der Bank, sowie der
Zeichenfolgen dddfr, DDDREF1, DDDREF2, 609, DDDKODIERZEILE nicht
- verschieben.</p></li></ul></div><div class="screenshot"><div class="mediaobject"><img src="images/Einzahlungsschein_Makro.png"></div></div></div><div class="sect3" title="2.13.1.5. Auswahl der Druckvorlage in kivitendo beim Erzeugen einer odt-Rechnung (analog bei Auftrag)"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2550"></a>2.13.1.5. Auswahl der Druckvorlage in kivitendo beim Erzeugen einer
+ verschieben.</p></li></ul></div><div class="screenshot"><div class="mediaobject"><img src="images/Einzahlungsschein_Makro.png"></div></div></div><div class="sect3" title="2.13.1.5. Auswahl der Druckvorlage in kivitendo beim Erzeugen einer odt-Rechnung (analog bei Auftrag)"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2544"></a>2.13.1.5. Auswahl der Druckvorlage in kivitendo beim Erzeugen einer
odt-Rechnung (analog bei Auftrag)</h4></div></div></div><p>Im Fussbereich der Rechnungsmaske muss neben Rechnung,
OpenDocument/OASIS und Bildschirm die im Adminbereich erstellte
Druckerbeschreibung ausgewählt werden, falls diese nicht bereits bei
den Benutzereinstellungen als persönlicher Standard gewählt
- wurde.</p></div><div class="sect3" title="2.13.1.6. Makroeinstellungen in LibreOffice anpassen"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2555"></a>2.13.1.6. Makroeinstellungen in LibreOffice anpassen</h4></div></div></div><p>Falls beim Öffnen einer von kivitendo erzeugten odt-Rechnung
+ wurde.</p></div><div class="sect3" title="2.13.1.6. Makroeinstellungen in LibreOffice anpassen"><div class="titlepage"><div><div><h4 class="title"><a name="d0e2549"></a>2.13.1.6. Makroeinstellungen in LibreOffice anpassen</h4></div></div></div><p>Falls beim Öffnen einer von kivitendo erzeugten odt-Rechnung
die Meldung kommt, dass Makros aus Sicherheitsgründen nicht
ausgeführt werden, so müssen folgende Einstellungen in LibreOffice
angepasst werden:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Extras → Optionen → Sicherheit → Makrosicherheit</p></li><li class="listitem"><p>Sicherheitslevel auf "Mittel" einstellen (Diese
bis zu welchem Monat und Jahr die aktuelle Abrechnungsperiode dauert:
<code class="literal">Abrechnungszeitrum: <%period_start_date FORMAT=%m/%Y%>
bis <%period_end_date FORMAT=%m/%Y%></code>
- </p></div><div class="sect2" title="3.1.4. Auflisten"><div class="titlepage"><div><div><h3 class="title"><a name="features.periodic-invoices.reports"></a>3.1.4. Auflisten</h3></div></div></div><p>Unter Verkauf->Berichte->Aufträge finden sich zwei neue
+ </p><p>Beim automatischen Versand der Rechnugen via E-Mail können neben diesen speziellen Variablen auch einige Eigenschaften der
+ Rechnung selber als Variablen im Betreff & dem Text der E-Mails genutzt werden. Beispiele sind
+ <code class="varname"><%invnumber%></code> für die Rechnungsnummber oder <code class="varname"><transaction_description%></code> für die
+ Vorgangsbezeichnung. Diese Variablen stehen beim Erzeugen der Rechnung logischerweise noch nicht zur Verfügung.</p></div><div class="sect2" title="3.1.4. Auflisten"><div class="titlepage"><div><div><h3 class="title"><a name="features.periodic-invoices.reports"></a>3.1.4. Auflisten</h3></div></div></div><p>Unter Verkauf->Berichte->Aufträge finden sich zwei neue
Checkboxen, "Wiederkehrende Rechnungen aktiv" und "Wiederkehrende
Rechnungen inaktiv", mit denen man sich einen Überglick über die
wiederkehrenden Rechnungen verschaffen kann.</p></div><div class="sect2" title="3.1.5. Erzeugung der eigentlichen Rechnungen"><div class="titlepage"><div><div><h3 class="title"><a name="features.periodic-invoices.task-server"></a>3.1.5. Erzeugung der eigentlichen Rechnungen</h3></div></div></div><p>Die zeitliche und periodische Überprüfung, ob eine
<code class="varname">invdate</code>
</span></dt><dd><p>Rechnungsdatum</p></dd><dt><span class="term">
<code class="varname">invnumber</code>
- </span></dt><dd><p>Rechnungsnummer</p></dd></dl></div></div></div><div class="sect2" title="3.3.10. Variablen in anderen Vorlagen"><div class="titlepage"><div><div><h3 class="title"><a name="dokumentenvorlagen-und-variablen.andere-vorlagen"></a>3.3.10. Variablen in anderen Vorlagen</h3></div></div></div><div class="sect3" title="3.3.10.1. Einführung"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5833"></a>3.3.10.1. Einführung</h4></div></div></div><p>Die Variablen in anderen Vorlagen sind ähnlich wie in der
+ </span></dt><dd><p>Rechnungsnummer</p></dd></dl></div></div></div><div class="sect2" title="3.3.10. Variablen in anderen Vorlagen"><div class="titlepage"><div><div><h3 class="title"><a name="dokumentenvorlagen-und-variablen.andere-vorlagen"></a>3.3.10. Variablen in anderen Vorlagen</h3></div></div></div><div class="sect3" title="3.3.10.1. Einführung"><div class="titlepage"><div><div><h4 class="title"><a name="d0e5835"></a>3.3.10.1. Einführung</h4></div></div></div><p>Die Variablen in anderen Vorlagen sind ähnlich wie in der
Rechnung. Allerdings heißen die Variablen, die mit
<code class="varname">inv</code> beginnen, jetzt anders. Bei den Angeboten
fangen sie mit <code class="varname">quo</code> für "quotation" an:
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>3.7. Artikelklassifizierung</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="up" href="ch03.html" title="Kapitel 3. Features und Funktionen"><link rel="prev" href="ch03s06.html" title="3.6. Schweizer Kontenpläne"><link rel="next" href="ch03s08.html" title="3.8. Dateiverwaltung (Mini-DMS)"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">3.7. Artikelklassifizierung</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s06.html">Zurück</a> </td><th width="60%" align="center">Kapitel 3. Features und Funktionen</th><td width="20%" align="right"> <a accesskey="n" href="ch03s08.html">Weiter</a></td></tr></table><hr></div><div class="sect1" title="3.7. Artikelklassifizierung"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="features.part_classification"></a>3.7. Artikelklassifizierung</h2></div></div></div><div class="sect2" title="3.7.1. Übersicht"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6515"></a>3.7.1. Übersicht</h3></div></div></div><p>Die Klassifizierung von Artikeln dient einer weiteren
+ <title>3.7. Artikelklassifizierung</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="up" href="ch03.html" title="Kapitel 3. Features und Funktionen"><link rel="prev" href="ch03s06.html" title="3.6. Schweizer Kontenpläne"><link rel="next" href="ch03s08.html" title="3.8. Dateiverwaltung (Mini-DMS)"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">3.7. Artikelklassifizierung</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s06.html">Zurück</a> </td><th width="60%" align="center">Kapitel 3. Features und Funktionen</th><td width="20%" align="right"> <a accesskey="n" href="ch03s08.html">Weiter</a></td></tr></table><hr></div><div class="sect1" title="3.7. Artikelklassifizierung"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="features.part_classification"></a>3.7. Artikelklassifizierung</h2></div></div></div><div class="sect2" title="3.7.1. Übersicht"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6517"></a>3.7.1. Übersicht</h3></div></div></div><p>Die Klassifizierung von Artikeln dient einer weiteren
Gliederung, um zum Beispiel den Einkauf vom Verkauf zu trennen,
gekennzeichnet durch eine Beschreibung (z.B. "Einkauf") und ein Kürzel
(z.B. "E"). Für jede Klassifizierung besteht eine Beschreibung und
eine Abkürzung die normalerweise aus einem Zeichen besteht, kann aber
auf mehrere Zeichen erweitert werden, falls zur Unterscheidung
- notwendig. Sinnvoll sind jedoch nur maximal 2 Zeichen.</p></div><div class="sect2" title="3.7.2. Basisklassifizierung"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6520"></a>3.7.2. Basisklassifizierung</h3></div></div></div><p>Als Basisklassifizierungen gibt es</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Einkauf</p></li><li class="listitem"><p>Verkauf</p></li><li class="listitem"><p>Handelsware</p></li><li class="listitem"><p>Produktion</p></li><li class="listitem"><p>- keine - (diese wird bei einer Aktualisierung für alle
+ notwendig. Sinnvoll sind jedoch nur maximal 2 Zeichen.</p></div><div class="sect2" title="3.7.2. Basisklassifizierung"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6522"></a>3.7.2. Basisklassifizierung</h3></div></div></div><p>Als Basisklassifizierungen gibt es</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Einkauf</p></li><li class="listitem"><p>Verkauf</p></li><li class="listitem"><p>Handelsware</p></li><li class="listitem"><p>Produktion</p></li><li class="listitem"><p>- keine - (diese wird bei einer Aktualisierung für alle
existierenden Artikel verwendet und ist gültig für Verkauf und
Einkauf)</p></li></ul></div><p>Es können weitere Klassifizierungen angelegt werden. So kann es
- z.B. für separat auszuweisende Artikel folgende Klassen geben:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Lieferung (Logistik, Transport) mit Kürzel L</p></li><li class="listitem"><p>Material (Verpackungsmaterial) mit Kürzel M</p></li></ul></div></div><div class="sect2" title="3.7.3. Attribute"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6550"></a>3.7.3. Attribute</h3></div></div></div><p>Bisher haben die Klassifizierungen folgende Attribute, die auch
+ z.B. für separat auszuweisende Artikel folgende Klassen geben:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Lieferung (Logistik, Transport) mit Kürzel L</p></li><li class="listitem"><p>Material (Verpackungsmaterial) mit Kürzel M</p></li></ul></div></div><div class="sect2" title="3.7.3. Attribute"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6552"></a>3.7.3. Attribute</h3></div></div></div><p>Bisher haben die Klassifizierungen folgende Attribute, die auch
alle gleichzeitg gültig sein können</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>gültig für Verkauf - dieser Artikel kann im Verkauf genutzt
werden</p></li><li class="listitem"><p>gültig für Einkauf - dieser Artikel kann im Einkauf genutzt
werden</p></li><li class="listitem"><p>separat ausweisen - hierzu gibt es zur Dokumentengenerierung
pro separat auszuweisenden Klassifizierungen die Variable<span class="bold"><strong>< %separate_X_subtotal%></strong></span>, wobei X das
Kürzel der Klassifizierung ist.</p><p>Im obigen Beispiel wäre das für Lieferkosten <span class="bold"><strong><%separate_L_subtotal%></strong></span> und für
Verpackungsmaterial <span class="bold"><strong>
- <%separate_M_subtotal%></strong></span>.</p></div><div class="sect2" title="3.7.4. Zwei-Zeichen Abkürzung"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6581"></a>3.7.4. Zwei-Zeichen Abkürzung</h3></div></div></div><p>Der Typ des Artikels und die Klassifizierung werden durch zwei
+ <%separate_M_subtotal%></strong></span>.</p></div><div class="sect2" title="3.7.4. Zwei-Zeichen Abkürzung"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6583"></a>3.7.4. Zwei-Zeichen Abkürzung</h3></div></div></div><p>Der Typ des Artikels und die Klassifizierung werden durch zwei
Buchstaben dargestellt. Der erste Buchstabe ist eine Lokalisierung des
Artikel-Typs ('P','A','S'), deutsch 'W', 'E', und 'D' für Ware
Erzeugnis oder Dienstleistung und ggf. weiterer Typen.</p><p>Der zweite Buchstabe (und ggf. auch ein dritter, falls nötig)
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>3.8. Dateiverwaltung (Mini-DMS)</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="up" href="ch03.html" title="Kapitel 3. Features und Funktionen"><link rel="prev" href="ch03s07.html" title="3.7. Artikelklassifizierung"><link rel="next" href="ch03s09.html" title="3.9. Webshop-Api"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">3.8. Dateiverwaltung (Mini-DMS)</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s07.html">Zurück</a> </td><th width="60%" align="center">Kapitel 3. Features und Funktionen</th><td width="20%" align="right"> <a accesskey="n" href="ch03s09.html">Weiter</a></td></tr></table><hr></div><div class="sect1" title="3.8. Dateiverwaltung (Mini-DMS)"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="features.file_managment"></a>3.8. Dateiverwaltung (Mini-DMS)</h2></div></div></div><div class="sect2" title="3.8.1. Übersicht"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6593"></a>3.8.1. Übersicht</h3></div></div></div><p>Parallel zum alten WebDAV gibt es ein Datei-Management-System,
+ <title>3.8. Dateiverwaltung (Mini-DMS)</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="up" href="ch03.html" title="Kapitel 3. Features und Funktionen"><link rel="prev" href="ch03s07.html" title="3.7. Artikelklassifizierung"><link rel="next" href="ch03s09.html" title="3.9. Webshop-Api"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">3.8. Dateiverwaltung (Mini-DMS)</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s07.html">Zurück</a> </td><th width="60%" align="center">Kapitel 3. Features und Funktionen</th><td width="20%" align="right"> <a accesskey="n" href="ch03s09.html">Weiter</a></td></tr></table><hr></div><div class="sect1" title="3.8. Dateiverwaltung (Mini-DMS)"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="features.file_managment"></a>3.8. Dateiverwaltung (Mini-DMS)</h2></div></div></div><div class="sect2" title="3.8.1. Übersicht"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6595"></a>3.8.1. Übersicht</h3></div></div></div><p>Parallel zum alten WebDAV gibt es ein Datei-Management-System,
das Dateien verschiedenen Typs verwaltet. Dies können</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>aus ERP-Daten per LaTeX Template erzeugte
PDF-Dokumente,</p></li><li class="listitem"><p>zu bestimmten ERP-Daten gehörende Anhangdateien
unterschiedlichen Formats,</p></li><li class="listitem"><p>per Scanner eingelesene PDF-Dateien,</p></li><li class="listitem"><p>per E-Mail empfangene Dateianhänge unterschiedlichen
- Formats,</p></li><li class="listitem"><p>sowie speziel für Artikel hochgeladene Bilder sein.</p></li></ol></div><div class="screenshot"><div class="mediaobject"><img src="images/DMS-Overview.png"></div></div></div><div class="sect2" title="3.8.2. Struktur"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6620"></a>3.8.2. Struktur</h3></div></div></div><p>Über eine vom Speichermedium unabhängige Zwischenschicht werden
+ Formats,</p></li><li class="listitem"><p>sowie speziel für Artikel hochgeladene Bilder sein.</p></li></ol></div><div class="screenshot"><div class="mediaobject"><img src="images/DMS-Overview.png"></div></div></div><div class="sect2" title="3.8.2. Struktur"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6622"></a>3.8.2. Struktur</h3></div></div></div><p>Über eine vom Speichermedium unabhängige Zwischenschicht werden
die Dateien und ihre Versionen in der Datenbank verwaltet. Darunter
können verschiedene Implementierungen (Backends) gleichzeitig
existieren:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Dateisystem</p></li><li class="listitem"><p>WebDAV</p></li><li class="listitem"><p>Schnittstelle zu externen
für "attachment" und "image" nur die Quelle "uploaded". Für "document"
gibt es auf jeden Fall die Quelle "created". Die Quellen "scanner" und
"email" müssen derzeit in der Datenbank konfiguriert werden (siehe
- <a class="xref" href="ch03s08.html#file_management.dbconfig" title="3.8.4.2. Datenbank-Konfigurierung">Datenbank-Konfigurierung</a>).</p></div><div class="sect2" title="3.8.3. Anwendung"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6672"></a>3.8.3. Anwendung</h3></div></div></div><p>Die Daten werden bei den ERP-Objekten als extra Reiter
+ <a class="xref" href="ch03s08.html#file_management.dbconfig" title="3.8.4.2. Datenbank-Konfigurierung">Datenbank-Konfigurierung</a>).</p></div><div class="sect2" title="3.8.3. Anwendung"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6674"></a>3.8.3. Anwendung</h3></div></div></div><p>Die Daten werden bei den ERP-Objekten als extra Reiter
dargestellt. Eine Verkaufsrechnung z.B. hat die Reiter "Dokumente" und
"Dateianhänge".</p><div class="screenshot"><div class="mediaobject"><img src="images/DMS-Anhaenge.png"></div></div><p>Bei den Dateianhängen wird immer nur die aktuelle Version einer
Datei angezeigt. Wird eine Datei mit gleichem Namen hochgeladen, so
so sind diese z.B. bei Einkaufsrechnungen sichtbar:</p><div class="screenshot"><div class="mediaobject"><img src="images/DMS-Dokumente-Scanner.png"></div></div><p>Statt des Löschens wird hier die Datei zurück zur Quelle
verschoben. Somit kann die Datei anschließend an ein anderes
ERP-Objekt angehängt werden.</p><p>Derzeit sind "Titel" und "Beschreibung" noch nicht genutzt. Sie
- sind bisher nur bei Bildern relevant.</p></div><div class="sect2" title="3.8.4. Konfigurierung"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6715"></a>3.8.4. Konfigurierung</h3></div></div></div><div class="sect3" title="3.8.4.1. Mandantenkonfiguration"><div class="titlepage"><div><div><h4 class="title"><a name="file_management.clientconfig"></a>3.8.4.1. Mandantenkonfiguration</h4></div></div></div><div class="sect4" title="3.8.4.1.1. Reiter "Features""><div class="titlepage"><div><div><h5 class="title"><a name="d0e6721"></a>3.8.4.1.1. Reiter "Features"</h5></div></div></div><p>Unter dem Reiter <span class="bold"><strong>Features</strong></span>
+ sind bisher nur bei Bildern relevant.</p></div><div class="sect2" title="3.8.4. Konfigurierung"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6717"></a>3.8.4. Konfigurierung</h3></div></div></div><div class="sect3" title="3.8.4.1. Mandantenkonfiguration"><div class="titlepage"><div><div><h4 class="title"><a name="file_management.clientconfig"></a>3.8.4.1. Mandantenkonfiguration</h4></div></div></div><div class="sect4" title="3.8.4.1.1. Reiter "Features""><div class="titlepage"><div><div><h5 class="title"><a name="d0e6723"></a>3.8.4.1.1. Reiter "Features"</h5></div></div></div><p>Unter dem Reiter <span class="bold"><strong>Features</strong></span>
im Abschnitt Dateimanagement ist neben dem "alten" WebDAV das
Dateimangement generell zu- und abschaltbar, sowie die Zuordnung
der Dateitypen zu Backends. Die Löschbarkeit von Dateien, sowie
die maximale Uploadgröße sind Backend-unabhängig</p><div class="screenshot"><div class="mediaobject"><img src="images/DMS-ClientConfig.png"></div></div><p>Die einzelnen Backends sind einzeln einschaltbar.
Spezifische Backend-Konfigurierungen sind hier noch
- ergänzbar.</p></div><div class="sect4" title="3.8.4.1.2. Reiter "Allgemeine Dokumentenanhänge""><div class="titlepage"><div><div><h5 class="title"><a name="d0e6737"></a>3.8.4.1.2. Reiter "Allgemeine Dokumentenanhänge"</h5></div></div></div><p>Unter dem Reiter <span class="bold"><strong>Allgemeine
+ ergänzbar.</p></div><div class="sect4" title="3.8.4.1.2. Reiter "Allgemeine Dokumentenanhänge""><div class="titlepage"><div><div><h5 class="title"><a name="d0e6739"></a>3.8.4.1.2. Reiter "Allgemeine Dokumentenanhänge"</h5></div></div></div><p>Unter dem Reiter <span class="bold"><strong>Allgemeine
Dokumentenanhänge</strong></span> kann für alle ERP-Dokumente (
Angebote, Aufträge, Lieferscheine, Rechnungen im Verkauf und
Einkauf ) allgemeingültige Anhänge hochgeladen werden.</p><div class="screenshot"><div class="mediaobject"><img src="images/DMS-Allgemeine-Dokumentenanhaenge.png"></div></div><p>Diese Anhänge werden beim Generieren von PDF-Dateien an die
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>3.9. Webshop-Api</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="up" href="ch03.html" title="Kapitel 3. Features und Funktionen"><link rel="prev" href="ch03s08.html" title="3.8. Dateiverwaltung (Mini-DMS)"><link rel="next" href="ch03s10.html" title="3.10. ZUGFeRD Rechnungen"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">3.9. Webshop-Api</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s08.html">Zurück</a> </td><th width="60%" align="center">Kapitel 3. Features und Funktionen</th><td width="20%" align="right"> <a accesskey="n" href="ch03s10.html">Weiter</a></td></tr></table><hr></div><div class="sect1" title="3.9. Webshop-Api"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e6771"></a>3.9. Webshop-Api</h2></div></div></div><p>Das Shopmodul bietet die Möglichkeit Onlineshopartikel und
+ <title>3.9. Webshop-Api</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="up" href="ch03.html" title="Kapitel 3. Features und Funktionen"><link rel="prev" href="ch03s08.html" title="3.8. Dateiverwaltung (Mini-DMS)"><link rel="next" href="ch03s10.html" title="3.10. ZUGFeRD Rechnungen"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">3.9. Webshop-Api</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s08.html">Zurück</a> </td><th width="60%" align="center">Kapitel 3. Features und Funktionen</th><td width="20%" align="right"> <a accesskey="n" href="ch03s10.html">Weiter</a></td></tr></table><hr></div><div class="sect1" title="3.9. Webshop-Api"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e6773"></a>3.9. Webshop-Api</h2></div></div></div><p>Das Shopmodul bietet die Möglichkeit Onlineshopartikel und
Onlineshopbestellungen zu verwalten und zu bearbeiten.</p><p>Es ist Multishopfähig, d.h. Artikel können mehreren oder
unterschiedlichen Shops zugeordnet werden. Bestellungen können aus
mehreren Shops geholt werden.</p><p>Zur Zeit bietet das Modul nur einen Connector zur REST-Api von
Shopware. Weitere Connectoren können dazu programmiert und eingerichtet
- werden.</p><div class="sect2" title="3.9.1. Rechte für die Webshopapi"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6780"></a>3.9.1. Rechte für die Webshopapi</h3></div></div></div><p>In der Administration können folgende Rechte vergeben
- werden</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Webshopartikel anlegen und bearbeiten</p></li><li class="listitem"><p>Shopbestellungen holen und bearbeiten</p></li><li class="listitem"><p>Shop anlegen und bearbeiten</p></li></ul></div></div><div class="sect2" title="3.9.2. Konfiguration"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6795"></a>3.9.2. Konfiguration</h3></div></div></div><p>Unter System->Webshops können Shops angelegt und konfiguriert
- werden</p><div class="mediaobject"><img src="images/Shop_Listing.png"></div></div><div class="sect2" title="3.9.3. Webshopartikel"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6803"></a>3.9.3. Webshopartikel</h3></div></div></div><div class="sect3" title="3.9.3.1. Shopvariablenreiter in Artikelstammdaten"><div class="titlepage"><div><div><h4 class="title"><a name="d0e6806"></a>3.9.3.1. Shopvariablenreiter in Artikelstammdaten</h4></div></div></div><p>Mit dem Recht "Shopartikel anlegen und bearbeiten" und des
+ werden.</p><div class="sect2" title="3.9.1. Rechte für die Webshopapi"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6782"></a>3.9.1. Rechte für die Webshopapi</h3></div></div></div><p>In der Administration können folgende Rechte vergeben
+ werden</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Webshopartikel anlegen und bearbeiten</p></li><li class="listitem"><p>Shopbestellungen holen und bearbeiten</p></li><li class="listitem"><p>Shop anlegen und bearbeiten</p></li></ul></div></div><div class="sect2" title="3.9.2. Konfiguration"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6797"></a>3.9.2. Konfiguration</h3></div></div></div><p>Unter System->Webshops können Shops angelegt und konfiguriert
+ werden</p><div class="mediaobject"><img src="images/Shop_Listing.png"></div></div><div class="sect2" title="3.9.3. Webshopartikel"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6805"></a>3.9.3. Webshopartikel</h3></div></div></div><div class="sect3" title="3.9.3.1. Shopvariablenreiter in Artikelstammdaten"><div class="titlepage"><div><div><h4 class="title"><a name="d0e6808"></a>3.9.3.1. Shopvariablenreiter in Artikelstammdaten</h4></div></div></div><p>Mit dem Recht "Shopartikel anlegen und bearbeiten" und des
Markers <span class="bold"><strong>"Shopartikel" in den Basisdaten
</strong></span>zeigt sich der Reiter "Shopvariablen" in den
Artikelstammdaten. Hier können jetzt die Artikel mit
Stelle können auch beliebig viele Bilder dem Shopartikel zugeordnet
werden. Artikelbilder gelten für alle Shops.</p><div class="mediaobject"><img src="images/Shop_Artikel.png"></div><p>Die Artikelgruppen werden direkt vom Shopsystem geholt somit
ist es möglich einen Artikel auch mehreren Gruppen
- zuzuordenen</p></div><div class="sect3" title="3.9.3.2. Shopartikelliste"><div class="titlepage"><div><div><h4 class="title"><a name="d0e6819"></a>3.9.3.2. Shopartikelliste</h4></div></div></div><p>Unter dem Menu Webshop->Webshop Artikel hat man nochmal
+ zuzuordenen</p></div><div class="sect3" title="3.9.3.2. Shopartikelliste"><div class="titlepage"><div><div><h4 class="title"><a name="d0e6821"></a>3.9.3.2. Shopartikelliste</h4></div></div></div><p>Unter dem Menu Webshop->Webshop Artikel hat man nochmal
eine Gesamtübersicht. Von hier aus ist es möglich Artikel im Stapel
unter verschiedenen Kriterien <alles><nur Preis><nur
Bestand><Preis und Bestand> an die jeweiligen Shops
- hochzuladen.</p><div class="mediaobject"><img src="images/Shop_Artikel_Listing.png"></div></div></div><div class="sect2" title="3.9.4. Bestellimport"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6827"></a>3.9.4. Bestellimport</h3></div></div></div><p>Unter dem Menupunkt Webshop->Webshop Import öffnet sich die
+ hochzuladen.</p><div class="mediaobject"><img src="images/Shop_Artikel_Listing.png"></div></div></div><div class="sect2" title="3.9.4. Bestellimport"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6829"></a>3.9.4. Bestellimport</h3></div></div></div><p>Unter dem Menupunkt Webshop->Webshop Import öffnet sich die
Bestellimportsliste. Hier ist sind Möglichkeiten gegeben Neue
Bestellungen vom Shop abzuholen, geholte Bestellungen im Stapel oder
einzeln als Auftrag zu transferieren. Die Liste kann nach
auch der Grund für die Auftragssperre sein.</p></li><li class="listitem"><p>Die Buttons "Auftrag erstellen" und "Kunde mit
Rechnungsadresse überschreiben" zeigen sich erst, wenn ein Kunde
aus dem Listing ausgewählt ist.</p></li><li class="listitem"><p>Es ist aber möglich die Shopbestellung zu löschen.</p></li><li class="listitem"><p>Ist eine Bestellung schon übernommen, zeigen sich an dieser
- Stelle, die dazugehörigen Belegverknüpfungen.</p></li></ul></div></div><div class="sect2" title="3.9.5. Mapping der Daten"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6880"></a>3.9.5. Mapping der Daten</h3></div></div></div><p>Das Mapping der kivitendo Daten mit den Shopdaten geschieht in
+ Stelle, die dazugehörigen Belegverknüpfungen.</p></li></ul></div></div><div class="sect2" title="3.9.5. Mapping der Daten"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6882"></a>3.9.5. Mapping der Daten</h3></div></div></div><p>Das Mapping der kivitendo Daten mit den Shopdaten geschieht in
der Datei SL/ShopConnector/<SHOPCONNECTORNAME>.pm
z.B.:SL/ShopConnector/Shopware.pm</p><p>In dieser Datei gibt es einen Bereich wo die Bestellpostionen,
die Bestellkopfdaten und die Artikeldaten gemapt werden. In dieser
</p></div><div class="sect2" title="3.10.3. Erstellen von ZUGFeRD Rechnungen in Kivitendo"><div class="titlepage"><div><div><h3 class="title"><a name="features.zugferd.create_zugferd_bills"></a>3.10.3. Erstellen von ZUGFeRD Rechnungen in Kivitendo</h3></div></div></div><p>Für die Erstellung von ZUGFeRD Rechnungen bedarf es in
kivitendo zwei Dinge:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>Die Erstellung muss in der Mandantenkonfiguration
aktiviert sein</p></li><li class="listitem"><p>Beim mindestens einem Bankkonto muss die Option
- „Nutzung von ZUGFeRD“ aktiviert sein</p></li></ol></div><div class="sect3" title="3.10.3.1. Mandantenkonfiguration"><div class="titlepage"><div><div><h4 class="title"><a name="d0e6927"></a>3.10.3.1. Mandantenkonfiguration</h4></div></div></div><p>Die Einstellung für die Erstellung von ZUGFeRD Rechnungen
+ „Nutzung von ZUGFeRD“ aktiviert sein</p></li></ol></div><div class="sect3" title="3.10.3.1. Mandantenkonfiguration"><div class="titlepage"><div><div><h4 class="title"><a name="d0e6929"></a>3.10.3.1. Mandantenkonfiguration</h4></div></div></div><p>Die Einstellung für die Erstellung von ZUGFeRD Rechnungen
erfolgt unter „System“ → „Mandatenkonfiguration“ → „Features“.
Im Abschnitt „Einkauf und Verkauf“ finden Sie die Einstellung
„Verkaufsrechnungen mit ZUGFeRD-Daten erzeugen“.
Hier besteht die Auswahl zwischen:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>ZUGFeRD-Rechnungen erzeugen</p></li><li class="listitem"><p>ZUGFeRD-Rechnungen im Testmodus erzeugen</p></li><li class="listitem"><p>Keine ZUGFeRD Rechnungen erzeugen</p></li></ul></div><p>Rechnungen die als PDF erzeugt werden, werden je nach
- Einstellung nun im ZUGFeRD Format ausgegeben.</p></div><div class="sect3" title="3.10.3.2. Konfiguration der Bankkonten"><div class="titlepage"><div><div><h4 class="title"><a name="d0e6944"></a>3.10.3.2. Konfiguration der Bankkonten</h4></div></div></div><p>Unter „System → Bankkonten“ muss bei mindestens einem
+ Einstellung nun im ZUGFeRD Format ausgegeben.</p></div><div class="sect3" title="3.10.3.2. Konfiguration der Bankkonten"><div class="titlepage"><div><div><h4 class="title"><a name="d0e6946"></a>3.10.3.2. Konfiguration der Bankkonten</h4></div></div></div><p>Unter „System → Bankkonten“ muss bei mindestens einem
Bankkonto die Option „Nutzung mit ZUGFeRD“ auf „Ja“ gestellt
werden.</p></div></div><div class="sect2" title="3.10.4. Einlesen von ZUGFeRD Rechnungen in Kivitendo"><div class="titlepage"><div><div><h3 class="title"><a name="features.zugferd.read_zugferd_bills"></a>3.10.4. Einlesen von ZUGFeRD Rechnungen in Kivitendo</h3></div></div></div><p>Es lassen sich auch Rechnungen von Kreditoren, die im
ZUGFeRD Format erstellt wurden, nach Kivitendo importieren.
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>Kapitel 4. Entwicklerdokumentation</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="up" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="prev" href="ch03s10.html" title="3.10. ZUGFeRD Rechnungen"><link rel="next" href="ch04s02.html" title="4.2. Entwicklung unter FastCGI"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Kapitel 4. Entwicklerdokumentation</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s10.html">Zurück</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="ch04s02.html">Weiter</a></td></tr></table><hr></div><div class="chapter" title="Kapitel 4. Entwicklerdokumentation"><div class="titlepage"><div><div><h2 class="title"><a name="d0e6963"></a>Kapitel 4. Entwicklerdokumentation</h2></div></div></div><div class="sect1" title="4.1. Globale Variablen"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="devel.globals"></a>4.1. Globale Variablen</h2></div></div></div><div class="sect2" title="4.1.1. Wie sehen globale Variablen in Perl aus?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6969"></a>4.1.1. Wie sehen globale Variablen in Perl aus?</h3></div></div></div><p>Globale Variablen liegen in einem speziellen namespace namens
+ <title>Kapitel 4. Entwicklerdokumentation</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="up" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="prev" href="ch03s10.html" title="3.10. ZUGFeRD Rechnungen"><link rel="next" href="ch04s02.html" title="4.2. Entwicklung unter FastCGI"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Kapitel 4. Entwicklerdokumentation</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch03s10.html">Zurück</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="ch04s02.html">Weiter</a></td></tr></table><hr></div><div class="chapter" title="Kapitel 4. Entwicklerdokumentation"><div class="titlepage"><div><div><h2 class="title"><a name="d0e6965"></a>Kapitel 4. Entwicklerdokumentation</h2></div></div></div><div class="sect1" title="4.1. Globale Variablen"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="devel.globals"></a>4.1. Globale Variablen</h2></div></div></div><div class="sect2" title="4.1.1. Wie sehen globale Variablen in Perl aus?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e6971"></a>4.1.1. Wie sehen globale Variablen in Perl aus?</h3></div></div></div><p>Globale Variablen liegen in einem speziellen namespace namens
"main", der von überall erreichbar ist. Darüber hinaus sind bareword
globs global und die meisten speziellen Variablen sind...
speziell.</p><p>Daraus ergeben sich folgende Formen:</p><div class="variablelist"><dl><dt><span class="term">
<code class="varname">$PACKAGE::form</code>.</p></dd><dt><span class="term">
<code class="literal">local $form</code>
</span></dt><dd><p>Alle Änderungen an <code class="varname">$form</code> werden am Ende
- des scopes zurückgesetzt</p></dd></dl></div></div><div class="sect2" title="4.1.2. Warum sind globale Variablen ein Problem?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e7070"></a>4.1.2. Warum sind globale Variablen ein Problem?</h3></div></div></div><p>Das erste Problem ist <span class="productname">FCGI</span>™.</p><p>
+ des scopes zurückgesetzt</p></dd></dl></div></div><div class="sect2" title="4.1.2. Warum sind globale Variablen ein Problem?"><div class="titlepage"><div><div><h3 class="title"><a name="d0e7072"></a>4.1.2. Warum sind globale Variablen ein Problem?</h3></div></div></div><p>Das erste Problem ist <span class="productname">FCGI</span>™.</p><p>
<span class="productname">SQL-Ledger</span>â„¢ hat fast alles im globalen
namespace abgelegt, und erwartet, dass es da auch wiederzufinden ist.
Unter <span class="productname">FCGI</span>™ müssen diese Sachen aber wieder
dies hat, seit der Einführung, u.a. schon so manche langwierige
Bug-Suche verkürzt. Da globale Variablen aber implizit mit Package
angegeben werden, werden die nicht geprüft, und somit kann sich
- schnell ein Tippfehler einschleichen.</p></div><div class="sect2" title="4.1.3. Kanonische globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e7103"></a>4.1.3. Kanonische globale Variablen</h3></div></div></div><p>Um dieses Problem im Griff zu halten gibt es einige wenige
+ schnell ein Tippfehler einschleichen.</p></div><div class="sect2" title="4.1.3. Kanonische globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e7105"></a>4.1.3. Kanonische globale Variablen</h3></div></div></div><p>Um dieses Problem im Griff zu halten gibt es einige wenige
globale Variablen, die kanonisch sind, d.h. sie haben bestimmte
vorgegebenen Eigenschaften, und alles andere sollte anderweitig
umhergereicht werden.</p><p>Diese Variablen sind im Moment die folgenden neun:</p><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
<code class="varname">$::request</code>
</p></li></ul></div><p>Damit diese nicht erneut als Müllhalde missbraucht werden, im
Folgenden eine kurze Erläuterung der bestimmten vorgegebenen
- Eigenschaften (Konventionen):</p><div class="sect3" title="4.1.3.1. $::form"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7167"></a>4.1.3.1. $::form</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Ist ein Objekt der Klasse
+ Eigenschaften (Konventionen):</p><div class="sect3" title="4.1.3.1. $::form"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7169"></a>4.1.3.1. $::form</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Ist ein Objekt der Klasse
"<code class="classname">Form</code>"</p></li><li class="listitem"><p>Wird nach jedem Request gelöscht</p></li><li class="listitem"><p>Muss auch in Tests und Konsolenscripts vorhanden
sein.</p></li><li class="listitem"><p>Enthält am Anfang eines Requests die Requestparameter vom
User</p></li><li class="listitem"><p>Kann zwar intern über Requestgrenzen ein Datenbankhandle
push @{ $form->{TEMPLATE_ARRAYS}{number} }, $form->{"partnumber_$i"};
push @{ $form->{TEMPLATE_ARRAYS}{description} }, $form->{"description_$i"};
# ...
-}</pre></div><div class="sect3" title="4.1.3.2. %::myconfig"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7251"></a>4.1.3.2. %::myconfig</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Das einzige Hash unter den globalen Variablen</p></li><li class="listitem"><p>Wird spätestens benötigt wenn auf die Datenbank
+}</pre></div><div class="sect3" title="4.1.3.2. %::myconfig"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7253"></a>4.1.3.2. %::myconfig</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Das einzige Hash unter den globalen Variablen</p></li><li class="listitem"><p>Wird spätestens benötigt wenn auf die Datenbank
zugegriffen wird</p></li><li class="listitem"><p>Wird bei jedem Request neu erstellt.</p></li><li class="listitem"><p>Enthält die Userdaten des aktuellen Logins</p></li><li class="listitem"><p>Sollte nicht ohne Filterung irgendwo gedumpt werden oder
extern serialisiert werden, weil da auch der Datenbankzugriff
für diesen user drinsteht.</p></li><li class="listitem"><p>Enthält unter anderem Datumsformat dateformat und
überwiegend die Daten, die sich unter <span class="guimenu">Programm</span>
-> <span class="guimenuitem">Einstellungen</span> befinden, bzw. die
Informationen über den Benutzer die über die
- Administrator-Schnittstelle eingegeben wurden.</p></div><div class="sect3" title="4.1.3.3. $::locale"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7290"></a>4.1.3.3. $::locale</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "Locale"</p></li><li class="listitem"><p>Wird pro Request erstellt</p></li><li class="listitem"><p>Muss auch für Tests und Scripte immer verfügbar
+ Administrator-Schnittstelle eingegeben wurden.</p></div><div class="sect3" title="4.1.3.3. $::locale"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7292"></a>4.1.3.3. $::locale</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "Locale"</p></li><li class="listitem"><p>Wird pro Request erstellt</p></li><li class="listitem"><p>Muss auch für Tests und Scripte immer verfügbar
sein.</p></li><li class="listitem"><p>Cached intern über Requestgrenzen hinweg benutzte
Locales</p></li></ul></div><p>Lokalisierung für den aktuellen User. Alle Übersetzungen,
- Zahlen- und Datumsformatierungen laufen über dieses Objekt.</p></div><div class="sect3" title="4.1.3.4. $::lxdebug"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7308"></a>4.1.3.4. $::lxdebug</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "LXDebug"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Muss immer verfügbar sein, in nahezu allen
+ Zahlen- und Datumsformatierungen laufen über dieses Objekt.</p></div><div class="sect3" title="4.1.3.4. $::lxdebug"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7310"></a>4.1.3.4. $::lxdebug</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "LXDebug"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Muss immer verfügbar sein, in nahezu allen
Funktionen</p></li></ul></div><p>
<code class="varname">$::lxdebug</code> stellt Debuggingfunktionen
bereit, wie "<code class="function">enter_sub</code>" und
"<code class="function">message</code>" und "<code class="function">dump</code>" mit
denen man flott Informationen ins Log (tmp/kivitendo-debug.log)
packen kann.</p><p>Beispielsweise so:</p><pre class="programlisting">$main::lxdebug->message(0, 'Meine Konfig:' . Dumper (%::myconfig));
-$main::lxdebug->message(0, 'Wer bin ich? Kunde oder Lieferant:' . $form->{vc});</pre></div><div class="sect3" title="4.1.3.5. $::auth"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7345"></a>4.1.3.5. $::auth</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "SL::Auth"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Hat eine permanente DB Verbindung zur Authdatenbank</p></li><li class="listitem"><p>Wird nach jedem Request resettet.</p></li></ul></div><p>
+$main::lxdebug->message(0, 'Wer bin ich? Kunde oder Lieferant:' . $form->{vc});</pre></div><div class="sect3" title="4.1.3.5. $::auth"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7347"></a>4.1.3.5. $::auth</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse "SL::Auth"</p></li><li class="listitem"><p>Wird global gecached</p></li><li class="listitem"><p>Hat eine permanente DB Verbindung zur Authdatenbank</p></li><li class="listitem"><p>Wird nach jedem Request resettet.</p></li></ul></div><p>
<code class="varname">$::auth</code> stellt Funktionen bereit um die
Rechte des aktuellen Users abzufragen. Obwohl diese Informationen
vom aktuellen User abhängen wird das Objekt aus
Dessen Einstellungen können über
<code class="literal">$::auth->client</code> abgefragt werden; Rückgabewert
ist ein Hash mit den Werten aus der Tabelle
- <code class="literal">auth.clients</code>.</p></div><div class="sect3" title="4.1.3.6. $::lx_office_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7374"></a>4.1.3.6. $::lx_office_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
+ <code class="literal">auth.clients</code>.</p></div><div class="sect3" title="4.1.3.6. $::lx_office_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7376"></a>4.1.3.6. $::lx_office_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
"<code class="classname">SL::LxOfficeConf</code>"</p></li><li class="listitem"><p>Global gecached</p></li><li class="listitem"><p>Repräsentation der
<code class="filename">config/kivitendo.conf[.default]</code>-Dateien</p></li></ul></div><p>Globale Konfiguration. Configdateien werden zum Start gelesen
und danach nicht mehr angefasst. Es ist derzeit nicht geplant, dass
file_name = /tmp/kivitendo-debug.log</pre><p>ist der Key <code class="varname">file</code> im Programm als
<code class="varname">$::lx_office_conf->{debug}{file}</code>
erreichbar.</p><div class="warning" title="Warnung" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warnung]" src="system/docbook-xsl/images/warning.png"></td><th align="left">Warnung</th></tr><tr><td align="left" valign="top"><p>Zugriff auf die Konfiguration erfolgt im Moment über
- Hashkeys, sind also nicht gegen Tippfehler abgesichert.</p></td></tr></table></div></div><div class="sect3" title="4.1.3.7. $::instance_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7410"></a>4.1.3.7. $::instance_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
+ Hashkeys, sind also nicht gegen Tippfehler abgesichert.</p></td></tr></table></div></div><div class="sect3" title="4.1.3.7. $::instance_conf"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7412"></a>4.1.3.7. $::instance_conf</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
"<code class="classname">SL::InstanceConfiguration</code>"</p></li><li class="listitem"><p>wird pro Request neu erstellt</p></li></ul></div><p>Funktioniert wie <code class="varname">$::lx_office_conf</code>,
speichert aber Daten die von der Instanz abhängig sind. Eine Instanz
ist hier eine Mandantendatenbank. Beispielsweise überprüft
</p><pre class="programlisting">$::instance_conf->get_inventory_system eq 'perpetual'</pre><p>
- ob die berüchtigte Bestandsmethode zur Anwendung kommt.</p></div><div class="sect3" title="4.1.3.8. $::dispatcher"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7431"></a>4.1.3.8. $::dispatcher</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
+ ob die berüchtigte Bestandsmethode zur Anwendung kommt.</p></div><div class="sect3" title="4.1.3.8. $::dispatcher"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7433"></a>4.1.3.8. $::dispatcher</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Objekt der Klasse
"<code class="varname">SL::Dispatcher</code>"</p></li><li class="listitem"><p>wird pro Serverprozess erstellt.</p></li><li class="listitem"><p>enthält Informationen über die technische Verbindung zum
Server</p></li></ul></div><p>Der dritte Punkt ist auch der einzige Grund warum das Objekt
global gespeichert wird. Wird vermutlich irgendwann in einem anderen
- Objekt untergebracht.</p></div><div class="sect3" title="4.1.3.9. $::request"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7449"></a>4.1.3.9. $::request</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Hashref (evtl später Objekt)</p></li><li class="listitem"><p>Wird pro Request neu initialisiert.</p></li><li class="listitem"><p>Keine Unterstruktur garantiert.</p></li></ul></div><p>
+ Objekt untergebracht.</p></div><div class="sect3" title="4.1.3.9. $::request"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7451"></a>4.1.3.9. $::request</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Hashref (evtl später Objekt)</p></li><li class="listitem"><p>Wird pro Request neu initialisiert.</p></li><li class="listitem"><p>Keine Unterstruktur garantiert.</p></li></ul></div><p>
<code class="varname">$::request</code> ist ein generischer Platz um
Daten "für den aktuellen Request" abzulegen. Sollte nicht für action
at a distance benutzt werden, sondern um lokales memoizing zu
<code class="varname">$::request</code>
</p></li><li class="listitem"><p>Muss ich von anderen Teilen des Programms lesend drauf
zugreifen? Dann <code class="varname">$::request</code>, aber Zugriff über
- Wrappermethode</p></li></ul></div></div></div><div class="sect2" title="4.1.4. Ehemalige globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e7491"></a>4.1.4. Ehemalige globale Variablen</h3></div></div></div><p>Die folgenden Variablen waren einmal im Programm, und wurden
- entfernt.</p><div class="sect3" title="4.1.4.1. $::cgi"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7496"></a>4.1.4.1. $::cgi</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil cookie Methoden nicht als
+ Wrappermethode</p></li></ul></div></div></div><div class="sect2" title="4.1.4. Ehemalige globale Variablen"><div class="titlepage"><div><div><h3 class="title"><a name="d0e7493"></a>4.1.4. Ehemalige globale Variablen</h3></div></div></div><p>Die folgenden Variablen waren einmal im Programm, und wurden
+ entfernt.</p><div class="sect3" title="4.1.4.1. $::cgi"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7498"></a>4.1.4.1. $::cgi</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil cookie Methoden nicht als
Klassenfunktionen funktionieren</p></li><li class="listitem"><p>Aufruf als Klasse erzeugt Dummyobjekt was im
Klassennamespace gehalten wird und über Requestgrenzen
leaked</p></li><li class="listitem"><p>liegt jetzt unter
<code class="varname">$::request->{cgi}</code>
- </p></li></ul></div></div><div class="sect3" title="4.1.4.2. $::all_units"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7512"></a>4.1.4.2. $::all_units</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil einige Funktionen in Schleifen zum Teil
+ </p></li></ul></div></div><div class="sect3" title="4.1.4.2. $::all_units"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7514"></a>4.1.4.2. $::all_units</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>war nötig, weil einige Funktionen in Schleifen zum Teil
ein paar hundert mal pro Request eine Liste der Einheiten
brauchen, und de als Parameter durch einen Riesenstack von
Funktionen geschleift werden müssten.</p></li><li class="listitem"><p>Liegt jetzt unter
<code class="varname">$::request->{cache}{all_units}</code>
</p></li><li class="listitem"><p>Wird nur in
<code class="function">AM->retrieve_all_units()</code> gesetzt oder
- gelesen.</p></li></ul></div></div><div class="sect3" title="4.1.4.3. %::called_subs"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7531"></a>4.1.4.3. %::called_subs</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>wurde benutzt um callsub deep recursions
+ gelesen.</p></li></ul></div></div><div class="sect3" title="4.1.4.3. %::called_subs"><div class="titlepage"><div><div><h4 class="title"><a name="d0e7533"></a>4.1.4.3. %::called_subs</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>wurde benutzt um callsub deep recursions
abzufangen.</p></li><li class="listitem"><p>Wurde entfernt, weil callsub nur einen Bruchteil der
möglichen Rekursioenen darstellt, und da nie welche
auftreten.</p></li><li class="listitem"><p>komplette recursion protection wurde entfernt.</p></li></ul></div></div></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch03s10.html">Zurück</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="ch04s02.html">Weiter</a></td></tr><tr><td width="40%" align="left" valign="top">3.10. ZUGFeRD Rechnungen </td><td width="20%" align="center"><a accesskey="h" href="index.html">Zum Anfang</a></td><td width="40%" align="right" valign="top"> 4.2. Entwicklung unter FastCGI</td></tr></table></div></body></html>
\ No newline at end of file
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung</title><link rel="stylesheet" type="text/css" href="style.css"><meta name="generator" content="DocBook XSL Stylesheets V1.76.1-RC2"><link rel="home" href="index.html" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><link rel="next" href="ch01.html" title="Kapitel 1. Aktuelle Hinweise"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">kivitendo 3.5.6.1: Installation, Konfiguration,
Entwicklung</th></tr><tr><td width="20%" align="left"> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="ch01.html">Weiter</a></td></tr></table><hr></div><div lang="de" class="book" title="kivitendo 3.5.6.1: Installation, Konfiguration, Entwicklung"><div class="titlepage"><div><div><h1 class="title"><a name="kivitendo-documentation"></a>kivitendo 3.5.6.1: Installation, Konfiguration,
- Entwicklung</h1></div></div><hr></div><div class="toc"><p><b>Inhaltsverzeichnis</b></p><dl><dt><span class="chapter"><a href="ch01.html">1. Aktuelle Hinweise</a></span></dt><dt><span class="chapter"><a href="ch02.html">2. Installation und Grundkonfiguration</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch02.html#Installation-%C3%9Cbersicht">2.1. Übersicht</a></span></dt><dt><span class="sect1"><a href="ch02s02.html">2.2. Benötigte Software und Pakete</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s02.html#Betriebssystem">2.2.1. Betriebssystem</a></span></dt><dt><span class="sect2"><a href="ch02s02.html#Pakete">2.2.2. Benötigte Perl-Pakete installieren</a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e649">2.2.3. Andere Pakete installieren</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s03.html">2.3. Manuelle Installation des Programmpaketes</a></span></dt><dt><span class="sect1"><a href="ch02s04.html">2.4. kivitendo-Konfigurationsdatei</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s04.html#config.config-file.introduction">2.4.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch02s04.html#config.config-file.sections-parameters">2.4.2. Abschnitte und Parameter</a></span></dt><dt><span class="sect2"><a href="ch02s04.html#config.config-file.prior-versions">2.4.3. Versionen vor 2.6.3</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s05.html">2.5. Anpassung der PostgreSQL-Konfiguration</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s05.html#Zeichens%C3%A4tze-die-Verwendung-von-UTF-8">2.5.1. Zeichensätze/die Verwendung von Unicode/UTF-8</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#%C3%84nderungen-an-Konfigurationsdateien">2.5.2. Änderungen an Konfigurationsdateien</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#Erweiterung-f%C3%BCr-servergespeicherte-Prozeduren">2.5.3. Erweiterung für servergespeicherte Prozeduren</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#Erweiterung-f%C3%BCr-trigram">2.5.4. Erweiterung für Trigram Prozeduren</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#Datenbankbenutzer-anlegen">2.5.5. Datenbankbenutzer anlegen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s06.html">2.6. Webserver-Konfiguration</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s06.html#d0e1129">2.6.1. Grundkonfiguration mittels CGI</a></span></dt><dt><span class="sect2"><a href="ch02s06.html#Apache-Konfiguration.FCGI">2.6.2. Konfiguration für FastCGI/FCGI</a></span></dt><dt><span class="sect2"><a href="ch02s06.html#d0e1283">2.6.3. Authentifizierung mittels HTTP Basic Authentication</a></span></dt><dt><span class="sect2"><a href="ch02s06.html#d0e1299">2.6.4. Aktivierung von mod_rewrite/directory_match für git basierte Installationen</a></span></dt><dt><span class="sect2"><a href="ch02s06.html#d0e1313">2.6.5. Weitergehende Konfiguration</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s07.html">2.7. Der Task-Server</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s07.html#Konfiguration-des-Task-Servers">2.7.1. Verfügbare und notwendige Konfigurationsoptionen</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Konfiguration-der-Mandanten-fuer-den-Task-Servers">2.7.2. Konfiguration der Mandanten für den Task-Server</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Einbinden-in-den-Boot-Prozess">2.7.3. Automatisches Starten des Task-Servers beim Booten</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Prozesskontrolle">2.7.4. Wie der Task-Server gestartet und beendet wird</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Tasks konfigurieren">2.7.5. Exemplarische Konfiguration eines Hintergrund-Jobs, der die Jahreszahl in allen Nummernkreisen zum Jahreswechsel erhöht</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s08.html">2.8. Benutzerauthentifizierung und Administratorpasswort</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s08.html#Grundlagen-zur-Benutzerauthentifizierung">2.8.1. Grundlagen zur Benutzerauthentifizierung</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Administratorpasswort">2.8.2. Administratorpasswort</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Authentifizierungsdatenbank">2.8.3. Authentifizierungsdatenbank</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Passwort%C3%BCberpr%C3%BCfung">2.8.4. Passwortüberprüfung</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Name-des-Session-Cookies">2.8.5. Name des Session-Cookies</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Anlegen-der-Authentifizierungsdatenbank">2.8.6. Anlegen der Authentifizierungsdatenbank</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s09.html">2.9. Mandanten-, Benutzer- und Gruppenverwaltung</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s09.html#Zusammenh%C3%A4nge">2.9.1. Zusammenhänge</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Mandanten-Benutzer-Gruppen">2.9.2. Mandanten, Benutzer und Gruppen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Datenbanken-anlegen">2.9.3. Datenbanken anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Gruppen-anlegen">2.9.4. Gruppen anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Benutzer-anlegen">2.9.5. Benutzer anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Mandanten-anlegen">2.9.6. Mandanten anlegen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s10.html">2.10. Drucker- und Systemverwaltung</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s10.html#Druckeradministration">2.10.1. Druckeradministration</a></span></dt><dt><span class="sect2"><a href="ch02s10.html#System">2.10.2. System sperren / entsperren</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s11.html">2.11. E-Mail-Versand aus kivitendo heraus</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s11.html#config.sending-email.sendmail">2.11.1. Versand über lokalen E-Mail-Server</a></span></dt><dt><span class="sect2"><a href="ch02s11.html#config.sending-email.smtp">2.11.2. Versand über einen SMTP-Server</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s12.html">2.12. Drucken mit kivitendo</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s12.html#Vorlagenverzeichnis-anlegen">2.12.1. Vorlagenverzeichnis anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s12.html#Vorlagen-RB">2.12.2. Der Druckvorlagensatz RB</a></span></dt><dt><span class="sect2"><a href="ch02s12.html#Vorlagen-rev-odt">2.12.3. Der Druckvorlagensatz rev-odt</a></span></dt><dt><span class="sect2"><a href="ch02s12.html#allgemeine-hinweise-zu-latex">2.12.4. Allgemeine Hinweise zu LaTeX Vorlagen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s13.html">2.13. OpenDocument-Vorlagen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s13.html#d0e2420">2.13.1. OpenDocument (odt) Druckvorlagen mit Makros</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s14.html">2.14. Nomenklatur</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s14.html#booking.dates">2.14.1. Datum bei Buchungen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s15.html">2.15. Konfiguration zur Einnahmenüberschussrechnung/Bilanzierung:
+ Entwicklung</h1></div></div><hr></div><div class="toc"><p><b>Inhaltsverzeichnis</b></p><dl><dt><span class="chapter"><a href="ch01.html">1. Aktuelle Hinweise</a></span></dt><dt><span class="chapter"><a href="ch02.html">2. Installation und Grundkonfiguration</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch02.html#Installation-%C3%9Cbersicht">2.1. Übersicht</a></span></dt><dt><span class="sect1"><a href="ch02s02.html">2.2. Benötigte Software und Pakete</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s02.html#Betriebssystem">2.2.1. Betriebssystem</a></span></dt><dt><span class="sect2"><a href="ch02s02.html#Pakete">2.2.2. Benötigte Perl-Pakete installieren</a></span></dt><dt><span class="sect2"><a href="ch02s02.html#d0e649">2.2.3. Andere Pakete installieren</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s03.html">2.3. Manuelle Installation des Programmpaketes</a></span></dt><dt><span class="sect1"><a href="ch02s04.html">2.4. kivitendo-Konfigurationsdatei</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s04.html#config.config-file.introduction">2.4.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch02s04.html#config.config-file.sections-parameters">2.4.2. Abschnitte und Parameter</a></span></dt><dt><span class="sect2"><a href="ch02s04.html#config.config-file.prior-versions">2.4.3. Versionen vor 2.6.3</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s05.html">2.5. Anpassung der PostgreSQL-Konfiguration</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s05.html#Zeichens%C3%A4tze-die-Verwendung-von-UTF-8">2.5.1. Zeichensätze/die Verwendung von Unicode/UTF-8</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#%C3%84nderungen-an-Konfigurationsdateien">2.5.2. Änderungen an Konfigurationsdateien</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#Erweiterung-f%C3%BCr-servergespeicherte-Prozeduren">2.5.3. Erweiterung für servergespeicherte Prozeduren</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#Erweiterung-f%C3%BCr-trigram">2.5.4. Erweiterung für Trigram Prozeduren</a></span></dt><dt><span class="sect2"><a href="ch02s05.html#Datenbankbenutzer-anlegen">2.5.5. Datenbankbenutzer anlegen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s06.html">2.6. Webserver-Konfiguration</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s06.html#d0e1123">2.6.1. Grundkonfiguration mittels CGI</a></span></dt><dt><span class="sect2"><a href="ch02s06.html#Apache-Konfiguration.FCGI">2.6.2. Konfiguration für FastCGI/FCGI</a></span></dt><dt><span class="sect2"><a href="ch02s06.html#d0e1277">2.6.3. Authentifizierung mittels HTTP Basic Authentication</a></span></dt><dt><span class="sect2"><a href="ch02s06.html#d0e1293">2.6.4. Aktivierung von mod_rewrite/directory_match für git basierte Installationen</a></span></dt><dt><span class="sect2"><a href="ch02s06.html#d0e1307">2.6.5. Weitergehende Konfiguration</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s07.html">2.7. Der Task-Server</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s07.html#Konfiguration-des-Task-Servers">2.7.1. Verfügbare und notwendige Konfigurationsoptionen</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Konfiguration-der-Mandanten-fuer-den-Task-Servers">2.7.2. Konfiguration der Mandanten für den Task-Server</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Einbinden-in-den-Boot-Prozess">2.7.3. Automatisches Starten des Task-Servers beim Booten</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Prozesskontrolle">2.7.4. Wie der Task-Server gestartet und beendet wird</a></span></dt><dt><span class="sect2"><a href="ch02s07.html#Tasks konfigurieren">2.7.5. Exemplarische Konfiguration eines Hintergrund-Jobs, der die Jahreszahl in allen Nummernkreisen zum Jahreswechsel erhöht</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s08.html">2.8. Benutzerauthentifizierung und Administratorpasswort</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s08.html#Grundlagen-zur-Benutzerauthentifizierung">2.8.1. Grundlagen zur Benutzerauthentifizierung</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Administratorpasswort">2.8.2. Administratorpasswort</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Authentifizierungsdatenbank">2.8.3. Authentifizierungsdatenbank</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Passwort%C3%BCberpr%C3%BCfung">2.8.4. Passwortüberprüfung</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Name-des-Session-Cookies">2.8.5. Name des Session-Cookies</a></span></dt><dt><span class="sect2"><a href="ch02s08.html#Anlegen-der-Authentifizierungsdatenbank">2.8.6. Anlegen der Authentifizierungsdatenbank</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s09.html">2.9. Mandanten-, Benutzer- und Gruppenverwaltung</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s09.html#Zusammenh%C3%A4nge">2.9.1. Zusammenhänge</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Mandanten-Benutzer-Gruppen">2.9.2. Mandanten, Benutzer und Gruppen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Datenbanken-anlegen">2.9.3. Datenbanken anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Gruppen-anlegen">2.9.4. Gruppen anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Benutzer-anlegen">2.9.5. Benutzer anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s09.html#Mandanten-anlegen">2.9.6. Mandanten anlegen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s10.html">2.10. Drucker- und Systemverwaltung</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s10.html#Druckeradministration">2.10.1. Druckeradministration</a></span></dt><dt><span class="sect2"><a href="ch02s10.html#System">2.10.2. System sperren / entsperren</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s11.html">2.11. E-Mail-Versand aus kivitendo heraus</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s11.html#config.sending-email.sendmail">2.11.1. Versand über lokalen E-Mail-Server</a></span></dt><dt><span class="sect2"><a href="ch02s11.html#config.sending-email.smtp">2.11.2. Versand über einen SMTP-Server</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s12.html">2.12. Drucken mit kivitendo</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s12.html#Vorlagenverzeichnis-anlegen">2.12.1. Vorlagenverzeichnis anlegen</a></span></dt><dt><span class="sect2"><a href="ch02s12.html#Vorlagen-RB">2.12.2. Der Druckvorlagensatz RB</a></span></dt><dt><span class="sect2"><a href="ch02s12.html#Vorlagen-rev-odt">2.12.3. Der Druckvorlagensatz rev-odt</a></span></dt><dt><span class="sect2"><a href="ch02s12.html#allgemeine-hinweise-zu-latex">2.12.4. Allgemeine Hinweise zu LaTeX Vorlagen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s13.html">2.13. OpenDocument-Vorlagen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s13.html#d0e2414">2.13.1. OpenDocument (odt) Druckvorlagen mit Makros</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s14.html">2.14. Nomenklatur</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s14.html#booking.dates">2.14.1. Datum bei Buchungen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s15.html">2.15. Konfiguration zur Einnahmenüberschussrechnung/Bilanzierung:
EUR</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s15.html#config.eur.introduction">2.15.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch02s15.html#config.eur.parameters">2.15.2. Konfigurationsparameter</a></span></dt><dt><span class="sect2"><a href="ch02s15.html#config.eur.setting-parameters">2.15.3. Festlegen der Parameter</a></span></dt><dt><span class="sect2"><a href="ch02s15.html#config.eur.inventory-system-perpetual">2.15.4. Bemerkungen zur Bestandsmethode</a></span></dt><dt><span class="sect2"><a href="ch02s15.html#config.eur.knonw-issues">2.15.5. Bekannte Probleme</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s16.html">2.16. SKR04 19% Umstellung für innergemeinschaftlichen Erwerb</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch02s16.html#config.skr04-update-3804.introduction">2.16.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch02s16.html#config.skr04-update-3804.create-chart">2.16.2. Konto 3804 manuell anlegen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch02s17.html">2.17. Verhalten des Bilanzberichts</a></span></dt><dt><span class="sect1"><a href="ch02s18.html">2.18. Erfolgsrechnung</a></span></dt><dt><span class="sect1"><a href="ch02s19.html">2.19. Rundung in Verkaufsbelegen</a></span></dt><dt><span class="sect1"><a href="ch02s20.html">2.20. Einstellungen pro Mandant</a></span></dt><dt><span class="sect1"><a href="ch02s21.html">2.21. kivitendo ERP verwenden</a></span></dt></dl></dd><dt><span class="chapter"><a href="ch03.html">3. Features und Funktionen</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch03.html#features.periodic-invoices">3.1. Wiederkehrende Rechnungen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.introduction">3.1.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.configuration">3.1.2. Konfiguration</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.variables">3.1.3. Spezielle Variablen</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.reports">3.1.4. Auflisten</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.task-server">3.1.5. Erzeugung der eigentlichen Rechnungen</a></span></dt><dt><span class="sect2"><a href="ch03.html#features.periodic-invoices.create-for-current-month">3.1.6. Erste Rechnung für aktuellen Monat erstellen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s02.html">3.2. Bankerweiterung</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s02.html#features.bank.introduction">3.2.1. Einführung</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s03.html">3.3. Dokumentenvorlagen und verfügbare Variablen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.einf%C3%BChrung">3.3.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.variablen-ausgeben">3.3.2. Variablen ausgeben</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.verwendung-in-druckbefehlen">3.3.3. Verwendung in Druckbefehlen</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.tag-style">3.3.4. Anfang und Ende der Tags verändern</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.zuordnung-dateinamen">3.3.5. Zuordnung von den Dateinamen zu den Funktionen</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.dateinamen-erweitert">3.3.6. Sprache, Drucker und E-Mail</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.allgemeine-variablen">3.3.7. Allgemeine Variablen, die in allen Vorlagen vorhanden
sind</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.invoice">3.3.8. Variablen in Rechnungen</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.dunning">3.3.9. Variablen in Mahnungen und Rechnungen über Mahngebühren</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.andere-vorlagen">3.3.10. Variablen in anderen Vorlagen</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.bloecke">3.3.11. Blöcke, bedingte Anweisungen und Schleifen</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.markup">3.3.12. Markup-Code zur Textformatierung innerhalb von
- Formularen</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.anrede">3.3.13. Hinweise zur Anrede</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s04.html">3.4. Excel-Vorlagen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s04.html#excel-templates.summary">3.4.1. Zusammenfassung</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#excel-templates.usage">3.4.2. Bedienung</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#excel-templates.syntax">3.4.3. Variablensyntax</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#excel-templates.limitations">3.4.4. Einschränkungen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s05.html">3.5. Mandantenkonfiguration Lager</a></span></dt><dt><span class="sect1"><a href="ch03s06.html">3.6. Schweizer Kontenpläne</a></span></dt><dt><span class="sect1"><a href="ch03s07.html">3.7. Artikelklassifizierung</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s07.html#d0e6515">3.7.1. Übersicht</a></span></dt><dt><span class="sect2"><a href="ch03s07.html#d0e6520">3.7.2. Basisklassifizierung</a></span></dt><dt><span class="sect2"><a href="ch03s07.html#d0e6550">3.7.3. Attribute</a></span></dt><dt><span class="sect2"><a href="ch03s07.html#d0e6581">3.7.4. Zwei-Zeichen Abkürzung</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s08.html">3.8. Dateiverwaltung (Mini-DMS)</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s08.html#d0e6593">3.8.1. Übersicht</a></span></dt><dt><span class="sect2"><a href="ch03s08.html#d0e6620">3.8.2. Struktur</a></span></dt><dt><span class="sect2"><a href="ch03s08.html#d0e6672">3.8.3. Anwendung</a></span></dt><dt><span class="sect2"><a href="ch03s08.html#d0e6715">3.8.4. Konfigurierung</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s09.html">3.9. Webshop-Api</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s09.html#d0e6780">3.9.1. Rechte für die Webshopapi</a></span></dt><dt><span class="sect2"><a href="ch03s09.html#d0e6795">3.9.2. Konfiguration</a></span></dt><dt><span class="sect2"><a href="ch03s09.html#d0e6803">3.9.3. Webshopartikel</a></span></dt><dt><span class="sect2"><a href="ch03s09.html#d0e6827">3.9.4. Bestellimport</a></span></dt><dt><span class="sect2"><a href="ch03s09.html#d0e6880">3.9.5. Mapping der Daten</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s10.html">3.10. ZUGFeRD Rechnungen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s10.html#features.zugferd.preamble">3.10.1. Vorbedingung</a></span></dt><dt><span class="sect2"><a href="ch03s10.html#features.zugferd.summary">3.10.2. Übersicht</a></span></dt><dt><span class="sect2"><a href="ch03s10.html#features.zugferd.create_zugferd_bills">3.10.3. Erstellen von ZUGFeRD Rechnungen in Kivitendo</a></span></dt><dt><span class="sect2"><a href="ch03s10.html#features.zugferd.read_zugferd_bills">3.10.4. Einlesen von ZUGFeRD Rechnungen in Kivitendo</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="ch04.html">4. Entwicklerdokumentation</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch04.html#devel.globals">4.1. Globale Variablen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04.html#d0e6969">4.1.1. Wie sehen globale Variablen in Perl aus?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e7070">4.1.2. Warum sind globale Variablen ein Problem?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e7103">4.1.3. Kanonische globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e7491">4.1.4. Ehemalige globale Variablen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s02.html">4.2. Entwicklung unter FastCGI</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.general">4.2.1. Allgemeines</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.exiting">4.2.2. Programmende und Ausnahmen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.globals">4.2.3. Globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.performance">4.2.4. Performance und Statistiken</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s03.html">4.3. Programmatische API-Aufrufe</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s03.html#dev-programmatic-api-calls.introduction">4.3.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#dev-programmatic-api-calls.client_selection">4.3.2. Wahl des Mandanten</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#dev-programmatic-api-calls.http_basic_authentication">4.3.3. HTTP-»Basic«-Authentifizierung</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#dev-programmatic-api-calls.authentication_via_parameters">4.3.4. Authentifizierung mit Parametern</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#dev-programmatic-api-calls.examples">4.3.5. Beispiele</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s04.html">4.4. SQL-Upgradedateien</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s04.html#db-upgrade-files.introduction">4.4.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#db-upgrade-files.format">4.4.2. Format der Kontrollinformationen</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#db-upgrade-files.format-perl-files">4.4.3. Format von in Perl geschriebenen
+ Formularen</a></span></dt><dt><span class="sect2"><a href="ch03s03.html#dokumentenvorlagen-und-variablen.anrede">3.3.13. Hinweise zur Anrede</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s04.html">3.4. Excel-Vorlagen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s04.html#excel-templates.summary">3.4.1. Zusammenfassung</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#excel-templates.usage">3.4.2. Bedienung</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#excel-templates.syntax">3.4.3. Variablensyntax</a></span></dt><dt><span class="sect2"><a href="ch03s04.html#excel-templates.limitations">3.4.4. Einschränkungen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s05.html">3.5. Mandantenkonfiguration Lager</a></span></dt><dt><span class="sect1"><a href="ch03s06.html">3.6. Schweizer Kontenpläne</a></span></dt><dt><span class="sect1"><a href="ch03s07.html">3.7. Artikelklassifizierung</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s07.html#d0e6517">3.7.1. Übersicht</a></span></dt><dt><span class="sect2"><a href="ch03s07.html#d0e6522">3.7.2. Basisklassifizierung</a></span></dt><dt><span class="sect2"><a href="ch03s07.html#d0e6552">3.7.3. Attribute</a></span></dt><dt><span class="sect2"><a href="ch03s07.html#d0e6583">3.7.4. Zwei-Zeichen Abkürzung</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s08.html">3.8. Dateiverwaltung (Mini-DMS)</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s08.html#d0e6595">3.8.1. Übersicht</a></span></dt><dt><span class="sect2"><a href="ch03s08.html#d0e6622">3.8.2. Struktur</a></span></dt><dt><span class="sect2"><a href="ch03s08.html#d0e6674">3.8.3. Anwendung</a></span></dt><dt><span class="sect2"><a href="ch03s08.html#d0e6717">3.8.4. Konfigurierung</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s09.html">3.9. Webshop-Api</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s09.html#d0e6782">3.9.1. Rechte für die Webshopapi</a></span></dt><dt><span class="sect2"><a href="ch03s09.html#d0e6797">3.9.2. Konfiguration</a></span></dt><dt><span class="sect2"><a href="ch03s09.html#d0e6805">3.9.3. Webshopartikel</a></span></dt><dt><span class="sect2"><a href="ch03s09.html#d0e6829">3.9.4. Bestellimport</a></span></dt><dt><span class="sect2"><a href="ch03s09.html#d0e6882">3.9.5. Mapping der Daten</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch03s10.html">3.10. ZUGFeRD Rechnungen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch03s10.html#features.zugferd.preamble">3.10.1. Vorbedingung</a></span></dt><dt><span class="sect2"><a href="ch03s10.html#features.zugferd.summary">3.10.2. Übersicht</a></span></dt><dt><span class="sect2"><a href="ch03s10.html#features.zugferd.create_zugferd_bills">3.10.3. Erstellen von ZUGFeRD Rechnungen in Kivitendo</a></span></dt><dt><span class="sect2"><a href="ch03s10.html#features.zugferd.read_zugferd_bills">3.10.4. Einlesen von ZUGFeRD Rechnungen in Kivitendo</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="ch04.html">4. Entwicklerdokumentation</a></span></dt><dd><dl><dt><span class="sect1"><a href="ch04.html#devel.globals">4.1. Globale Variablen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04.html#d0e6971">4.1.1. Wie sehen globale Variablen in Perl aus?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e7072">4.1.2. Warum sind globale Variablen ein Problem?</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e7105">4.1.3. Kanonische globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04.html#d0e7493">4.1.4. Ehemalige globale Variablen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s02.html">4.2. Entwicklung unter FastCGI</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.general">4.2.1. Allgemeines</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.exiting">4.2.2. Programmende und Ausnahmen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.globals">4.2.3. Globale Variablen</a></span></dt><dt><span class="sect2"><a href="ch04s02.html#devel.fcgi.performance">4.2.4. Performance und Statistiken</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s03.html">4.3. Programmatische API-Aufrufe</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s03.html#dev-programmatic-api-calls.introduction">4.3.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#dev-programmatic-api-calls.client_selection">4.3.2. Wahl des Mandanten</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#dev-programmatic-api-calls.http_basic_authentication">4.3.3. HTTP-»Basic«-Authentifizierung</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#dev-programmatic-api-calls.authentication_via_parameters">4.3.4. Authentifizierung mit Parametern</a></span></dt><dt><span class="sect2"><a href="ch04s03.html#dev-programmatic-api-calls.examples">4.3.5. Beispiele</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s04.html">4.4. SQL-Upgradedateien</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s04.html#db-upgrade-files.introduction">4.4.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#db-upgrade-files.format">4.4.2. Format der Kontrollinformationen</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#db-upgrade-files.format-perl-files">4.4.3. Format von in Perl geschriebenen
Datenbankupgradescripten</a></span></dt><dt><span class="sect2"><a href="ch04s04.html#db-upgrade-files.dbupgrade-tool">4.4.4. Hilfsscript dbupgrade2_tool.pl</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s05.html">4.5. Translations and languages</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s05.html#translations-languages.introduction">4.5.1. Introduction</a></span></dt><dt><span class="sect2"><a href="ch04s05.html#translations-languages.character-set">4.5.2. Character set</a></span></dt><dt><span class="sect2"><a href="ch04s05.html#translations-languages.file-structure">4.5.3. File structure</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s06.html">4.6. Die kivitendo-Test-Suite</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s06.html#devel.testsuite.intro">4.6.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s06.html#devel.testsuite.prerequisites">4.6.2. Voraussetzungen</a></span></dt><dt><span class="sect2"><a href="ch04s06.html#devel.testsuite.execution">4.6.3. Existierende Tests ausführen</a></span></dt><dt><span class="sect2"><a href="ch04s06.html#devel.testsuite.meaning_of_scripts">4.6.4. Bedeutung der verschiedenen Test-Scripte</a></span></dt><dt><span class="sect2"><a href="ch04s06.html#devel.testsuite.create_new">4.6.5. Neue Test-Scripte erstellen</a></span></dt></dl></dd><dt><span class="sect1"><a href="ch04s07.html">4.7. Stil-Richtlinien</a></span></dt><dt><span class="sect1"><a href="ch04s08.html">4.8. Dokumentation erstellen</a></span></dt><dd><dl><dt><span class="sect2"><a href="ch04s08.html#devel.build-doc.introduction">4.8.1. Einführung</a></span></dt><dt><span class="sect2"><a href="ch04s08.html#devel.build-doc.required-software">4.8.2. Benötigte Software</a></span></dt><dt><span class="sect2"><a href="ch04s08.html#devel.build-doc.build">4.8.3. PDFs und HTML-Seiten erstellen</a></span></dt><dt><span class="sect2"><a href="ch04s08.html#devel.build-doc.repository">4.8.4. Einchecken in das Git-Repository</a></span></dt></dl></dd></dl></dd></dl></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="ch01.html">Weiter</a></td></tr><tr><td width="40%" align="left" valign="top"> </td><td width="20%" align="center"> </td><td width="40%" align="right" valign="top"> Kapitel 1. Aktuelle Hinweise</td></tr></table></div></body></html>
\ No newline at end of file
// ## jQuery UI dialog plugin ##
- // Opening and closing and closing a popup
+ // Opening and closing a popup
else if (action[0] == 'dialog:open') kivi.popup_dialog(action[1]);
else if (action[0] == 'dialog:close') $(action[1]).dialog('close');
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 if (action[0] == 'set_cursor_position') kivi.set_cursor_position(action[1], action[2]);
else console.log('Unknown action: ' + action[0]);
return false;
};
- this.submitMassCreationForm = function() {
- if (!kivi.MassInvoiceCreatePrint.checkDeliveryOrderSelection())
- return false;
-
- $('body').addClass('loading');
- kivi.submit_form_with_action('form', 'MassInvoiceCreatePrint/create_invoices');
- return false;
- };
-
this.createPrintAllInitialize = function() {
kivi.popup_dialog({
id: 'create_print_all_dialog',
editor.on('instanceReady', function() { ns.focus_ckeditor($e); });
};
+ ns.filter_select = function() {
+ var $input = $(this);
+ var $select = $('#' + $input.data('select-id'));
+ var filter = $input.val().toLocaleLowerCase();
+
+ $select.find('option').each(function() {
+ if ($(this).text().toLocaleLowerCase().indexOf(filter) != -1)
+ $(this).show();
+ else
+ $(this).hide();
+ });
+ };
+
ns.reinit_widgets = function() {
ns.run_once_for('.datepicker', 'datepicker', function(elt) {
$(elt).datepicker();
kivi.ChartPicker($(elt));
});
+ ns.run_once_for('div.filtered_select input', 'filtered_select', function(elt) {
+ $(elt).bind('change keyup', ns.filter_select);
+ });
var func = kivi.get_function_by_name('local_reinit_widgets');
if (func)
$input.parent().replaceWith($area);
$area.focus();
};
+
+ ns.set_cursor_position = function(selector, position) {
+ var $input = $(selector);
+ if (position === 'end')
+ position = $input.val().length;
+
+ $input.prop('selectionStart', position);
+ $input.prop('selectionEnd', position);
+ };
});
kivi = namespace('kivi');
'All groups' => 'Alle Gruppen',
'All modules' => 'Alle Module',
'All partsgroups' => 'Alle Warengruppen',
+ 'All payments have already been posted.' => 'Es wurden bereits alle Zahlungen verbucht.',
+ 'All payments must be posted before the payment list can be downloaded.' => 'Alle Zahlungen müssen verbucht werden, bevor die Zahlungsliste heruntergeladen werden kann.',
'All price sources' => 'Alle Preisquellen',
'All reports' => 'Alle Berichte (Kontenübersicht, Summen- u. Saldenliste, Erfolgsrechnung, GuV, BWA, Bilanz, Projektbuchungen)',
'All the other clients will start with an empty set of WebDAV folders.' => 'Alle anderen Mandanten werden mit einem leeren Satz von Dokumenten-Ordnern ausgestattet.',
'All transactions' => 'Alle Buchungen',
'All units have either no or exactly one base unit of which they are multiples.' => 'Einheiten haben entweder keine oder genau eine Basiseinheit, von der sie ein Vielfaches sind.',
'All users' => 'Alle BenutzerInnen',
+ 'Allocations didn\'t pass constraints' => 'Keine Verfügbarkeit wegen Lagereinschränkung',
'Allow access' => 'Zugriff erlauben',
'Allow conversion from sales orders to sales invoices' => 'Umwandlung von Verkaufsaufträgen in Verkaufsrechnungen zulassen',
'Allow conversion from sales quotations to sales invoices' => 'Umwandlung von Verkaufsangeboten in Verkaufsrechnungen zulassen',
'Cancel Accounts Payables Transaction' => 'Kreditorenbuchung stornieren',
'Cancel Accounts Receivables Transaction' => 'Debitorenbuchung stornieren',
'Cancelling is disallowed. Either undo or balance the current payments until the open amount matches the invoice amount' => 'Storno verboten, da Zahlungen zum Beleg vorhanden sind. Entweder die Zahlungen löschen oder mit umgekehrten Vorzeichen ausbuchen, sodass der offene Betrag dem Rechnungsbetrag entspricht.',
+ 'Cannot allocate parts.' => 'Es sind nicht genügend Artikel vorhanden',
'Cannot change transaction in a closed period!' => 'In einem bereits abgeschlossenen Zeitraum kann keine Buchung verändert werden!',
'Cannot check correct WebDAV folder' => 'Kann nicht den richtigen WebDAV Pfad überprüfen',
'Cannot delete account!' => 'Konto kann nicht gelöscht werden!',
'Charge' => 'Berechnen',
'Charge Number' => 'Chargennummer',
'Charge number' => 'Chargennummer',
+ 'Chargenumbers' => 'Chargennummern',
'Charset' => 'Zeichensatz',
'Chart' => 'Buchungskonto',
'Chart Type' => 'Kontentyp',
'The first reason is that kivitendo contained a bug which resulted in the wrong taxkeys being recorded for transactions in which two entries are posted for the same chart with different taxkeys.' => 'Der erste Grund war ein Fehler in kivitendo, der dazu führte, dass bei einer Transaktion, bei der zwei Buchungen mit unterschiedlichen Steuerschlüsseln auf dasselbe Konto durchgeführt wurden, die falschen Steuerschlüssel gespeichert wurden.',
'The follow-up date is missing.' => 'Das Wiedervorlagedatum fehlt.',
'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 delivery orders could not be processed because they are already closed: #1' => 'Die folgenden Lieferscheine konnten nicht verarbeitet werden, da sie bereits geschlossen sind: #1',
'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 is only a preview.' => 'Das Folgende ist nur eine Vorschau.',
'brutto' => 'brutto',
'building data' => 'Verarbeite Daten',
'building report' => 'Erstelle Bericht',
+ 'can not allocate #1 units of #2, missing #3 units' => 'Kann keine #1 Einheiten von #2 belegen, es fehlen #3 Einheiten',
'can only parse a pdf file' => 'Kann nur eine gültige PDF-Datei verwenden.',
'cash' => 'Ist-Versteuerung',
'chargenumber #1' => 'Chargennummer #1',
'our vendor number at customer' => 'Unsere Lieferanten-Nr. beim Kunden',
'parsing csv' => 'Parse CSV Daten',
'part' => 'Ware',
+ 'part \'#\'1 in bin \'#2\' only with qty #3 (need additional #4) and chargenumber \'#5\'.' => 'Artikel \'#1\' im \'#2\' nur mit der Menge #3 (noch #4 benötig) und Chargennummer \'#5\'.',
'part_list' => 'Warenliste',
'percental' => 'prozentual',
'periodic' => 'Aufwandsmethode',
'Allow the following users access to my follow-ups:' => '',
'Allow to delete generated printfiles' => '',
'Already counted' => '',
+ 'Already imported entries (duplicates)' => '',
'Always edit assembly items (user can change/delete items even if assemblies are already produced)' => '',
'Always save orders with a projectnumber (create new projects)' => '',
'Amended Advance Turnover Tax Return' => '',
'Dunnings' => '',
'Dunnings (Id -- Dunning Date --Dunning Level -- Dunning Fee)' => '',
'Dunningstatistic' => '',
+ 'Duplicate' => '',
'Duplicate in CSV file' => '',
'Duplicate in database' => '',
'During the next update a taxkey 0 with tax rate of 0 will automatically created.' => '',
'Enter the requested execution date or leave empty for the quickest possible execution:' => '',
'Entries for which automatic conversion failed:' => '',
'Entries for which automatic conversion succeeded:' => '',
+ 'Entries ready to import' => '',
+ 'Entries with errors' => '',
'Equity' => '',
'Erfolgsrechnung' => '',
'Error' => '',
'Error: Invalid language' => '',
'Error: Invalid part' => '',
'Error: Invalid part type' => '',
- 'Error: Invalid parts group' => '',
+ 'Error: Invalid parts group id #1' => '',
+ 'Error: Invalid parts group name #1' => '',
'Error: Invalid payment terms' => '',
'Error: Invalid price factor' => '',
'Error: Invalid price group' => '',
'Import result' => '',
'Import scanned documents' => '',
'Importdate' => '',
+ 'Imported' => '',
+ 'Imported entries' => '',
'In addition to the above date functions, subtract the following amount of days from the calculated date as a buffer.' => '',
'In order to do that hit the button "Delete transaction".' => '',
'In order to migrate the old folder structure into the new structure you have to chose which client the old structure will be assigned to.' => '',
'Loading...' => '',
'Local Bank Code' => '',
'Local Tax Office Preferences' => '',
+ 'Local account' => '',
'Local account number' => '',
'Local bank account' => '',
'Local bank code' => '',
'MD' => '',
'MIME type' => '',
'MT940 import' => '',
+ 'MT940 import preview' => '',
+ 'MT940 import result' => '',
'Mails' => '',
'Main Contact Person' => '',
'Main Preferences' => '',
'No assembly has been selected yet.' => '',
'No background job has been created yet.' => '',
'No bank account chosen!' => '',
+ 'No bank account configured for bank code/BIC #1, account number/IBAN #2.' => '',
'No bank account flagged for ZUGFeRD usage was found.' => '',
'No bank information has been entered in this customer\'s master data entry. You cannot create bank collections unless you enter bank information.' => '',
'No bank information has been entered in this vendor\'s master data entry. You cannot create bank transfers unless you enter bank information.' => '',
'No email for user with login #1 defined.' => '',
'No email recipient for customer #1 defined.' => '',
'No end date given, setting to today' => '',
+ 'No entries can be imported.' => '',
'No entries have been imported yet.' => '',
'No entries have been selected.' => '',
'No errors have occurred.' => '',
'Orphaned' => '',
'Orphaned currencies' => '',
'Other Matches' => '',
+ 'Other party' => '',
'Other recipients' => '',
'Other users\' follow-ups' => '',
'Other values are ignored.' => '',
'Please contact your administrator or a service provider.' => '',
'Please contact your administrator.' => '',
'Please correct the settings and try again or deactivate that client.' => '',
- 'Please create a CSV import profile called "MT940" for the import type bank transactions:' => '',
'Please define a taxkey for the following taxes and run the update again:' => '',
'Please do so in the administration area.' => '',
'Please enter a profile name.' => '',
'Remittance information prefix' => '',
'Remote Bank Code' => '',
'Remote Name/Customer/Description' => '',
+ 'Remote account' => '',
'Remote account number' => '',
'Remote bank code' => '',
'Remote name' => '',
'The IBAN is missing.' => '',
'The ID #1 is not a valid database ID.' => '',
'The LDAP server "#1:#2" is unreachable. Please check config/kivitendo.conf.' => '',
- 'The MT940 import needs an import profile called MT940' => '',
'The Mail strings have been saved.' => '',
'The PDF has been created' => '',
'The PDF has been printed' => '',
'The first reason is that kivitendo contained a bug which resulted in the wrong taxkeys being recorded for transactions in which two entries are posted for the same chart with different taxkeys.' => '',
'The follow-up date is missing.' => '',
'The following currencies have been used, but they are not defined:' => '',
+ 'The following delivery orders could not be processed because they are already closed: #1' => '',
'The following drafts have been saved and can be loaded.' => '',
'The following groups are valid for this client' => '',
'The following is only a preview.' => '',
'To (time)' => '',
'To Date' => '',
'To continue please change the taxkey 0 to another value.' => '',
+ 'To import' => '',
'To upload images: Please create shoppart first' => '',
'To user login' => '',
'Toggle marker' => '',
'Total' => '',
'Total Fees' => '',
'Total Sales Orders Value' => '',
+ 'Total number of entries' => '',
'Total stock value' => '',
'Total sum' => '',
'Total weight' => '',
'Transaction ID missing.' => '',
'Transaction Value' => '',
'Transaction Value Currency Code' => '',
+ 'Transaction date' => '',
'Transaction deleted!' => '',
'Transaction description' => '',
'Transaction has already been cancelled!' => '',
'You have to grant users access to one or more clients.' => '',
'You have to specify a department.' => '',
'You have to specify an execution date for each antry.' => '',
+ 'You have to upload an MT940 file to import.' => '',
'You must chose a user.' => '',
'You must enter a name for your new print templates.' => '',
'You must not change this AP transaction.' => '',
'ZUGFeRD import' => '',
'ZUGFeRD invoice' => '',
'ZUGFeRD notes for each invoice' => '',
+ 'ZUGFeRD settings' => '',
'Zeitraum' => '',
'Zero amount posting!' => '',
'Zip' => '',
columns => [
dummy => { type => 'numeric', precision => 2, scale => 12 },
inty => { type => 'integer' },
+ miny => { type => 'integer' },
]
);
use SL::DB::Helper::AttrDuration;
__PACKAGE__->attr_duration('dummy');
-__PACKAGE__->attr_duration_minutes('inty');
+__PACKAGE__->attr_duration_minutes('inty', 'miny');
package main;
-use Test::More tests => 120;
+use Test::More tests => 130;
use Test::Exception;
use strict;
is($item->inty_as_hours, 3, 'write as_duration_string 03:1 read as_hours');
is($item->inty_as_duration_string, "3:01", 'write as_duration_string 03:1 read as_duration_string');
+local %::myconfig = (numberformat => "1.000,00");
+
+$item = new_item(miny_in_hours => 2.5);
+is($item->miny, 150, 'write in_hours 2.5 read raw');
+is($item->miny_as_minutes, 30, 'write in_hours 2.5 read as_minutes');
+is($item->miny_as_hours, 2, 'write in_hours 2.5 read as_hours');
+is($item->miny_in_hours, 2.5, 'write in_hours 2.5 read in_hours');
+is($item->miny_in_hours_as_number, '2,50', 'write in_hours 2.5 read in_hours_as_number');
+
+$item = new_item(miny_in_hours_as_number => '4,25');
+is($item->miny, 255, 'write in_hours_as_number 4,25 read raw');
+is($item->miny_as_minutes, 15, 'write in_hours_as_number 4,25 read as_minutes');
+is($item->miny_as_hours, 4, 'write in_hours_as_number 4,25 read as_hours');
+is($item->miny_in_hours, 4.25, 'write in_hours_as_number 4,25 read in_hours');
+is($item->miny_in_hours_as_number, '4,25', 'write in_hours_as_number 4,25 read in_hours_as_number');
+
# Parametervalidierung
throws_ok { new_item()->inty_as_duration_string('invalid') } qr/invalid.*format/i, 'invalid duration format';
--- /dev/null
+use strict;
+use Test::More;
+use Test::Exception;
+
+use lib 't';
+
+use SL::Dev::Part qw(new_part new_assembly);
+use SL::Dev::Inventory qw(create_warehouse_and_bins set_stock);
+use SL::Dev::Record qw(create_sales_order);
+
+use_ok 'Support::TestSetup';
+use_ok 'SL::DB::Bin';
+use_ok 'SL::DB::Part';
+use_ok 'SL::DB::Warehouse';
+use_ok 'SL::DB::Inventory';
+use_ok 'SL::WH';
+use_ok 'SL::Helper::Inventory';
+
+Support::TestSetup::login();
+
+my ($wh, $bin1, $bin2, $assembly1, $part1, $part2);
+
+reset_db();
+create_standard_stock();
+
+
+# simple stock in, get_stock, get_onhand
+set_stock(
+ part => $part1,
+ qty => 25,
+ bin => $bin1,
+);
+
+is(SL::Helper::Inventory::get_stock(part => $part1), "25.00000", 'simple get_stock works');
+is(SL::Helper::Inventory::get_onhand(part => $part1), "25.00000", 'simple get_onhand works');
+
+# stock on some more, get_stock, get_onhand
+
+WH->transfer({
+ parts_id => $part1->id,
+ qty => 15,
+ transfer_type => 'stock',
+ dst_warehouse_id => $bin1->warehouse_id,
+ dst_bin_id => $bin1->id,
+ comment => 'more',
+});
+
+WH->transfer({
+ parts_id => $part1->id,
+ qty => 20,
+ transfer_type => 'stock',
+ chargenumber => '298345',
+ dst_warehouse_id => $bin1->warehouse_id,
+ dst_bin_id => $bin1->id,
+ comment => 'more',
+});
+
+is(SL::Helper::Inventory::get_stock(part => $part1), "60.00000", 'normal get_stock works');
+is(SL::Helper::Inventory::get_onhand(part => $part1), "60.00000", 'normal get_onhand works');
+
+# allocate some stuff
+
+my @allocations = SL::Helper::Inventory::allocate(
+ part => $part1,
+ qty => 12,
+);
+
+is_deeply(\%{ $allocations[0] }, {
+ bestbefore => undef,
+ bin_id => $bin1->id,
+ chargenumber => '',
+ parts_id => $part1->id,
+ qty => 12,
+ warehouse_id => $wh->id,
+ comment => undef, # comment is not a partition so is not set by allocate
+ for_object_id => undef,
+ }, 'allocation works');
+
+# allocate something where more than one result will match
+
+@allocations = SL::Helper::Inventory::allocate(
+ part => $part1,
+ qty => 55,
+);
+
+is_deeply(\@allocations, [
+ {
+ bestbefore => undef,
+ bin_id => $bin1->id,
+ chargenumber => '',
+ parts_id => $part1->id,
+ qty => '40.00000',
+ warehouse_id => $wh->id,
+ comment => undef,
+ for_object_id => undef,
+ },
+ {
+ bestbefore => undef,
+ bin_id => $bin1->id,
+ chargenumber => '298345',
+ parts_id => $part1->id,
+ qty => '15',
+ warehouse_id => $wh->id,
+ comment => undef,
+ for_object_id => undef,
+ }
+], 'complex allocation works');
+
+# try to allocate too much
+
+dies_ok(sub {
+ SL::Helper::Inventory::allocate(part => $part1, qty => 100)
+},
+"allocate too much dies");
+
+# produce something
+
+reset_db();
+create_standard_stock();
+
+set_stock(
+ part => $part1,
+ qty => 5,
+ bin => $bin1,
+);
+set_stock(
+ part => $part2,
+ qty => 10,
+ bin => $bin1,
+);
+
+
+my @alloc1 = SL::Helper::Inventory::allocate(part => $part1, qty => 3);
+my @alloc2 = SL::Helper::Inventory::allocate(part => $part2, qty => 3);
+
+SL::Helper::Inventory::produce_assembly(
+ part => $assembly1,
+ qty => 3,
+ allocations => [ @alloc1, @alloc2 ],
+
+ # where to put it
+ bin => $bin1,
+ chargenumber => "537",
+);
+
+is(SL::Helper::Inventory::get_stock(part => $assembly1), "3.00000", 'produce works');
+is(SL::Helper::Inventory::get_stock(part => $part1), "2.00000", 'and consumes...');
+is(SL::Helper::Inventory::get_stock(part => $part2), "7.00000", '..the materials');
+
+# produce the same using auto_allocation
+
+reset_db();
+create_standard_stock();
+
+set_stock(
+ part => $part1,
+ qty => 5,
+ bin => $bin1,
+);
+set_stock(
+ part => $part2,
+ qty => 10,
+ bin => $bin1,
+);
+
+SL::Helper::Inventory::produce_assembly(
+ part => $assembly1,
+ qty => 3,
+ auto_allocate => 1,
+
+ # where to put it
+ bin => $bin1,
+ chargenumber => "537",
+);
+
+is(SL::Helper::Inventory::get_stock(part => $assembly1), "3.00000", 'produce with auto allocation works');
+is(SL::Helper::Inventory::get_stock(part => $part1), "2.00000", 'and consumes...');
+is(SL::Helper::Inventory::get_stock(part => $part2), "7.00000", '..the materials');
+
+# try to produce without allocations dies
+
+dies_ok(sub {
+SL::Helper::Inventory::produce_assembly(
+ part => $assembly1,
+ qty => 3,
+
+ # where to put it
+ bin => $bin1,
+ chargenumber => "537",
+);
+}, "producing without allocations dies");
+
+# try to produce with insufficient allocations dies
+
+@alloc1 = SL::Helper::Inventory::allocate(part => $part1, qty => 1);
+@alloc2 = SL::Helper::Inventory::allocate(part => $part2, qty => 1);
+
+dies_ok(sub {
+SL::Helper::Inventory::produce_assembly(
+ part => $assembly1,
+ qty => 3,
+ allocations => [ @alloc1, @alloc2 ],
+
+ # where to put it
+ bin => $bin1,
+ chargenumber => "537",
+);
+}, "producing with insufficient allocations dies");
+
+
+
+# bestbefore tests
+
+reset_db();
+create_standard_stock();
+
+set_stock(
+ part => $part1,
+ qty => 5,
+ bin => $bin1,
+);
+set_stock(
+ part => $part2,
+ qty => 10,
+ bin => $bin1,
+);
+
+
+
+SL::Helper::Inventory::produce_assembly(
+ part => $assembly1,
+ qty => 3,
+ auto_allocate => 1,
+
+ bin => $bin1,
+ chargenumber => "537",
+ bestbefore => DateTime->today->clone->add(days => -14), # expired 2 weeks ago
+ shippingdate => DateTime->today->clone->add(days => 1),
+);
+
+is(SL::Helper::Inventory::get_stock(part => $assembly1), "3.00000", 'produce with bestbefore works');
+is(SL::Helper::Inventory::get_onhand(part => $assembly1), "3.00000", 'produce with bestbefore works');
+is(SL::Helper::Inventory::get_stock(
+ part => $assembly1,
+ bestbefore => DateTime->today,
+), undef, 'get_stock with bestbefore date skips expired');
+{
+ local $::instance_conf->data->{show_bestbefore} = 1;
+ is(SL::Helper::Inventory::get_onhand(
+ part => $assembly1,
+ ), undef, 'get_onhand with bestbefore skips expired as of today');
+}
+
+{
+ local $::instance_conf->data->{show_bestbefore} = 0;
+ is(SL::Helper::Inventory::get_onhand(
+ part => $assembly1,
+ ), "3.00000", 'get_onhand without bestbefore finds all');
+}
+
+
+sub reset_db {
+ SL::DB::Manager::Order->delete_all(all => 1);
+ SL::DB::Manager::Inventory->delete_all(all => 1);
+ SL::DB::Manager::Assembly->delete_all(all => 1);
+ SL::DB::Manager::Part->delete_all(all => 1);
+ SL::DB::Manager::Bin->delete_all(all => 1);
+ SL::DB::Manager::Warehouse->delete_all(all => 1);
+}
+
+sub create_standard_stock {
+ ($wh, $bin1) = create_warehouse_and_bins();
+ $bin2 = SL::DB::Bin->new(description => "Bin 2", warehouse => $wh)->save;
+ $wh->load;
+
+ $assembly1 = new_assembly(number_of_parts => 2)->save;
+ ($part1, $part2) = map { $_->part } $assembly1->assemblies;
+}
+
+
+reset_db();
+
+done_testing();
+
+1;
\prg_new_conditional:Nnn \kivi_if_Price_col:n {T} {
\prop_get:cnN {l_kivi_col_#1_prop} {colspec} \l_tmpa_tl
- \tl_if_eq:NnTF \l_tmpa_tl {Price}
+ \exp_args:NV \tl_if_eq:nnTF \l_tmpa_tl {Price}
{\prg_return_true:}
{\prg_return_false:}
}
\dim_use:c {l_kivi_tab_##1_dim}+2\g_kivi_tabcolsep_dim
}
}
- \tl_gput_right:Nn \g_kivi_Pricing_colspec_tl {>{\raggedleft\arraybackslash}p{\dim_use:c {l_kivi_tab_##1_dim}}}
+ \tl_gput_right:Nn \g_kivi_Pricing_colspec_tl {K{\dim_use:c {l_kivi_tab_##1_dim}}}
\kivi_if_Price_col:nT {##1} {\tl_gput_right:Nn \g_kivi_Pricing_colspec_tl {<{\__kivi_tab_column_currency:}}}
}
}
\tl_gput_right:Nn \g_kivi_Pricing_colspec_tl {@{}}
}
-\newcolumntype{P}[1]{>{\raggedleft\arraybackslash}p{#1}<{\__kivi_tab_column_currency:}}
+\newcolumntype{K}[1]{>{\raggedleft\arraybackslash}p{#1}}
+\newcolumntype{P}[1]{K{#1}<{\__kivi_tab_column_currency:}}
\RequirePackage{tcolorbox}
\tcbuselibrary{breakable, skins}
<td nowrap>[% 'Customer Number' | $T8 %]</td>
<td align=right><input name="l_department" id="l_department" class=checkbox type=checkbox value=Y></td>
<td nowrap>[% 'Department' | $T8 %]</td>
-
+ </tr>
+ <tr>
+ <td align=right><input name="l_donumber" id="l_donumber" class=checkbox type=checkbox value=Y></td>
+ <td nowrap>[% 'Delivery Order Number' | $T8 %]</td>
+ <td align=right><input name="l_deliverydate" id="l_deliverydate" class=checkbox type=checkbox value=Y></td>
+ <td nowrap>[% 'Delivery Date' | $T8 %]</td>
</tr>
<tr>
<td align=right><input name="l_netamount" id="l_netamount" class=checkbox type=checkbox value="Y" checked></td>
</td>
</tr>
+ <tr>
+ <th align="right">[%- LxERP.t8('Date Format') %]:</th>
+ <td colspan="10">
+ [% L.select_tag('settings.dateformat', ['dd.mm.yyyy', 'yyyy-mm-dd', 'dd/mm/yyyy', 'mm/dd/yyyy' ], default = SELF.profile.get('dateformat'), style = 'width: 300px') %]
+ </td>
+ </tr>
+
<tr>
<th align="right">[%- LxERP.t8('Charset') %]:</th>
<td colspan="10">[% L.select_tag('settings.charset', SELF.all_charsets, default = SELF.profile.get('charset'), style = 'width: 300px') %]</td>
[%- INCLUDE 'common/flash.html' %]
-<script type="text/javascript" src="js/show_form_details.js"></script>
+<script type="text/javascript">
+ <!--
+ function copy_debit_to_credit() {
+ var txt = document.getElementsByName('debit_1')[0].value;
+ document.getElementsByName('credit_2')[0].value = txt;
+ };
+ //-->
+ </script>
+ <script type="text/javascript" src="js/show_form_details.js"></script>
<script type="text/javascript" src="js/follow_up.js"></script>
<script type="text/javascript" src="js/kivi.Draft.js"></script>
</tr>
<tr>
<th align="right" nowrap>[% 'Project Number' | $T8 %]</th>
- <td>[%- L.select_tag('globalproject_id', ALL_PROJECTS, title_key = 'projectnumber', default = globalproject_id, with_empty = '1', onChange = "document.getElementById('update_button').click();") %]</td>
+ <td>[% P.project.picker('globalproject_id', globalproject_id, onchange="document.getElementById('update_button').click();") %]</td>
</tr>
</table>
</td>
<tr>
<th align="right">[% 'Customer' | $T8 %]</th>
-<td>[% P.customer_vendor.picker('project.customer_id', SELF.project.customer_id, type='customer', fat_set_item=1, style='width: 300px', default=SELF.project.customer_id)%] [%- IF SELF.project.customer_id %]
-[%- END %]
-</td>
+ <td>[% P.customer_vendor.picker('project.customer_id', SELF.project.customer_id, type='customer', fat_set_item=1, style='width: 300px', default=SELF.project.customer_id)%]</td>
</tr>
<tr>