From: Moritz Bunkus Date: Fri, 27 Nov 2020 11:10:22 +0000 (+0100) Subject: Merge pull request #29 from TeXhackse/marei-reimplement-table X-Git-Tag: kivitendo-mebil_0.1-0~9^2~621 X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=0d3f708bfa179b6d0ea18d00398b98dd9557f389;hp=661febc70d58eb13923455512e8d76f1ef29ae60;p=kivitendo-erp.git Merge pull request #29 from TeXhackse/marei-reimplement-table Neuimplementierung und Feature Erweiterung für die Aktualisierten LaTeX Templates --- diff --git a/SL/DB/Helper/AttrDuration.pm b/SL/DB/Helper/AttrDuration.pm index 5bdd4bf7d..c627f190c 100644 --- a/SL/DB/Helper/AttrDuration.pm +++ b/SL/DB/Helper/AttrDuration.pm @@ -116,6 +116,22 @@ sub _make_minutes { 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; @@ -234,6 +250,17 @@ Access the full value as a formatted string in the form C, e.g. C<1:30> for the value 90 minutes. Parsing such a string is supported, too. +=item C + +Access the full value but convert to and from hours when +reading/writing the value. + +=item C + +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 diff --git a/SL/DB/Object.pm b/SL/DB/Object.pm index 3e0fca68b..89d15c671 100755 --- a/SL/DB/Object.pm +++ b/SL/DB/Object.pm @@ -7,6 +7,7 @@ use English qw(-no_match_vars); 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; @@ -105,6 +106,50 @@ sub update_attributes { 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; @@ -318,6 +363,28 @@ Assigns the attributes from C<%attributes> by calling the C function and saves the object afterwards. Returns the object itself. +=item C + +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 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 diff --git a/SL/Form.pm b/SL/Form.pm index 92d32e76f..46c52cb91 100644 --- a/SL/Form.pm +++ b/SL/Form.pm @@ -385,6 +385,7 @@ sub create_http_response { $session_cookie = $cgi->cookie('-name' => $main::auth->get_session_cookie_name(), '-value' => $session_cookie_value, '-path' => $uri->path, + '-expire' => '+' . ($::lx_office_conf{authentication}->{session_timeout} // 60) . 'm', '-secure' => $::request->is_https); } } diff --git a/t/db_helper/attr_duration.t b/t/db_helper/attr_duration.t index efc2d5402..0b023b66d 100644 --- a/t/db_helper/attr_duration.t +++ b/t/db_helper/attr_duration.t @@ -7,17 +7,18 @@ __PACKAGE__->meta->setup( 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; @@ -218,6 +219,22 @@ is($item->inty_as_minutes, 1, 'write as_duration_string 03:1 read a 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'; diff --git a/templates/webpages/project/_basic_data.html b/templates/webpages/project/_basic_data.html index 8c9f7096e..b2f2528f5 100644 --- a/templates/webpages/project/_basic_data.html +++ b/templates/webpages/project/_basic_data.html @@ -28,9 +28,7 @@ [% 'Customer' | $T8 %] -[% 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 %] - + [% 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)%]