]> wagnertech.de Git - kivitendo-erp.git/commitdiff
AttrDuration: Implementation für Spalten, die Dauer in Minuten speichern
authorMoritz Bunkus <m.bunkus@linet-services.de>
Fri, 3 Jul 2015 08:08:27 +0000 (10:08 +0200)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Fri, 3 Jul 2015 08:08:45 +0000 (10:08 +0200)
SL/DB/Helper/AttrDuration.pm
t/db_helper/attr_duration.t

index 704c340ae1d0ff301e310caef20f08e1be8a037d..5bdd4bf7d146ecb516b48acf7e3878cd0bd0f395 100644 (file)
@@ -3,7 +3,7 @@ package SL::DB::Helper::AttrDuration;
 use strict;
 
 use parent qw(Exporter);
 use strict;
 
 use parent qw(Exporter);
-our @EXPORT = qw(attr_duration);
+our @EXPORT = qw(attr_duration attr_duration_minutes);
 
 use Carp;
 
 
 use Carp;
 
@@ -13,6 +13,12 @@ sub attr_duration {
   _make($package, $_) for @attributes;
 }
 
   _make($package, $_) for @attributes;
 }
 
+sub attr_duration_minutes {
+  my ($package, @attributes) = @_;
+
+  _make_minutes($package, $_) for @attributes;
+}
+
 sub _make {
   my ($package, $attribute) = @_;
 
 sub _make {
   my ($package, $attribute) = @_;
 
@@ -75,6 +81,43 @@ sub _make {
   };
 }
 
   };
 }
 
+sub _make_minutes {
+  my ($package, $attribute) = @_;
+
+  no strict 'refs';
+
+  *{ $package . '::' . $attribute . '_as_hours' } = sub {
+    my ($self, $value) = @_;
+
+    $self->$attribute($value * 60 + ($self->$attribute % 60)) if @_ > 1;
+    return int(($self->$attribute // 0) / 60);
+  };
+
+  *{ $package . '::' . $attribute . '_as_minutes' } = sub {
+    my ($self, $value) = @_;
+
+    $self->$attribute(int($self->$attribute) - (int($self->$attribute) % 60) + ($value // 0)) if @_ > 1;
+    return ($self->$attribute // 0) % 60;
+  };
+
+  *{ $package . '::' . $attribute . '_as_duration_string' } = sub {
+    my ($self, $value) = @_;
+
+    if (@_ > 1) {
+      if (!defined($value) || ($value eq '')) {
+        $self->$attribute(undef);
+      } else {
+        croak $::locale->text("Invalid duration format") if $value !~ m{^(?:(\d*):)?(\d+)$};
+        $self->$attribute(($1 // 0) * 60 + ($2 // 0));
+      }
+    }
+
+    my $as_hours   = "${attribute}_as_hours";
+    my $as_minutes = "${attribute}_as_minutes";
+    return defined($self->$attribute) ? sprintf('%d:%02d', $self->$as_hours, $self->$as_minutes) : undef;
+  };
+}
+
 1;
 __END__
 
 1;
 __END__
 
@@ -92,6 +135,7 @@ numeric columns
   # In a Rose model:
   use SL::DB::Helper::AttrDuration;
   __PACKAGE__->attr_duration('time_estimation');
   # In a Rose model:
   use SL::DB::Helper::AttrDuration;
   __PACKAGE__->attr_duration('time_estimation');
+  __PACKAGE__->attr_duration_minutes('hours');
 
   # Read access:
   print "Minutes: " . $obj->time_estimation_as_minutes . " hours: " . $obj->time_estimation_as_hours . "\n";
 
   # Read access:
   print "Minutes: " . $obj->time_estimation_as_minutes . " hours: " . $obj->time_estimation_as_hours . "\n";
@@ -104,11 +148,23 @@ numeric columns
 
 =head1 OVERVIEW
 
 
 =head1 OVERVIEW
 
-This is a helper for columns that store a duration as a numeric or
-floating point number representing a number of hours. So the value
-1.75 would stand for "1 hour, 45 minutes".
+This is a helper for columns that store a duration in one of two formats:
 
 
-The helper methods created are:
+=over 2
+
+=item 1. as a numeric or floating point number representing a number
+of hours
+
+=item 2. as an integer presenting a number of minutes
+
+=back
+
+In the first case the value 1.75 would stand for "1 hour, 45
+minutes". In the second case the value 105 represents the same
+duration.
+
+The helper methods created depend on the mode. Calling
+C<attr_duration> makes the following methods available:
 
 =over 4
 
 
 =over 4
 
@@ -160,6 +216,26 @@ handles this case correctly.
 
 =back
 
 
 =back
 
+With C<attr_duration_minutes> the following methods are available:
+
+=over 4
+
+=item C<attribute_as_minutes [$new_value]>
+
+Access only the minutes. Return values are in the range [0 - 59].
+
+=item C<attribute_as_hours [$new_value]>
+
+Access only the hours. Returns an integer value.
+
+=item C<attribute_as_duration_string [$new_value]>
+
+Access the full value as a formatted string in the form C<h:mm>,
+e.g. C<1:30> for the value 90 minutes. Parsing such a string is
+supported, too.
+
+=back
+
 =head1 FUNCTIONS
 
 =over 4
 =head1 FUNCTIONS
 
 =over 4
@@ -169,6 +245,11 @@ handles this case correctly.
 Package method. Call with the names of attributes for which the helper
 methods should be created.
 
 Package method. Call with the names of attributes for which the helper
 methods should be created.
 
+=item C<attr_duration_minutes @attributes>
+
+Package method. Call with the names of attributes for which the helper
+methods should be created.
+
 =back
 
 =head1 BUGS
 =back
 
 =head1 BUGS
index da05a32114024ba3cd9108a163682cbdde275d26..0f41cfa037b1e4e0f12618bb83c68ffdc15c651d 100644 (file)
@@ -4,16 +4,20 @@ use base qw(SL::DB::Object);
 
 __PACKAGE__->meta->setup(
   table   => 'dummy',
 
 __PACKAGE__->meta->setup(
   table   => 'dummy',
-  columns => [ dummy => { type => 'numeric', precision => 2, scale => 12 }, ]
+  columns => [
+    dummy => { type => 'numeric', precision => 2, scale => 12 },
+    inty  => { type => 'integer' },
+  ]
 );
 
 use SL::DB::Helper::AttrDuration;
 
 __PACKAGE__->attr_duration('dummy');
 );
 
 use SL::DB::Helper::AttrDuration;
 
 __PACKAGE__->attr_duration('dummy');
+__PACKAGE__->attr_duration_minutes('inty');
 
 package main;
 
 
 package main;
 
-use Test::More tests => 91;
+use Test::More; # tests => 91;
 use Test::Exception;
 
 use strict;
 use Test::Exception;
 
 use strict;
@@ -31,6 +35,8 @@ sub new_item {
 Support::TestSetup::login();
 my $item;
 
 Support::TestSetup::login();
 my $item;
 
+### attr_duration
+
 # Wenn das Attribut undef ist:
 is(new_item->dummy,                    undef,  'uninitialized: raw');
 is(new_item->dummy_as_hours,           0,      'uninitialized: as_hours');
 # Wenn das Attribut undef ist:
 is(new_item->dummy,                    undef,  'uninitialized: raw');
 is(new_item->dummy_as_hours,           0,      'uninitialized: as_hours');
@@ -165,4 +171,51 @@ lives_ok  { new_item()->dummy_as_man_days_unit('h')       } 'known unit h';
 lives_ok  { new_item()->dummy_as_man_days_unit('hour')    } 'known unit hour';
 lives_ok  { new_item()->dummy_as_man_days_unit('man_day') } 'known unit man_day';
 
 lives_ok  { new_item()->dummy_as_man_days_unit('hour')    } 'known unit hour';
 lives_ok  { new_item()->dummy_as_man_days_unit('man_day') } 'known unit man_day';
 
+### attr_duration_minutes
+
+# Wenn das Attribut undef ist:
+is(new_item->inty,                    undef,  'uninitialized: raw');
+is(new_item->inty_as_hours,           0,      'uninitialized: as_hours');
+is(new_item->inty_as_minutes,         0,      'uninitialized: as_minutes');
+is(new_item->inty_as_duration_string, undef,  'uninitialized: as_duration_string');
+
+# Auslesen kleiner 60 Minuten:
+is(new_item(inty => 37)->inty,                    37,     'initialized < 60: raw');
+is(new_item(inty => 37)->inty_as_hours,           0,      'initialized < 60: as_hours');
+is(new_item(inty => 37)->inty_as_minutes,         37,     'initialized < 60: as_minutes');
+is(new_item(inty => 37)->inty_as_duration_string, '0:37', 'initialized < 60: as_duration_string');
+
+# Auslesen größer 60 Minuten:
+is(new_item(inty => 145)->inty,                    145,    'initialized > 60: raw');
+is(new_item(inty => 145)->inty_as_hours,           2,      'initialized > 60: as_hours');
+is(new_item(inty => 145)->inty_as_minutes,         25,     'initialized > 60: as_minutes');
+is(new_item(inty => 145)->inty_as_duration_string, '2:25', 'initialized > 60: as_duration_string');
+
+$item = new_item(inty => 145); $item->inty_as_duration_string(undef);
+is($item->inty,                    undef, 'write as_duration_string undef read raw');
+is($item->inty_as_minutes,         0,     'write as_duration_string undef read as_minutes');
+is($item->inty_as_hours,           0,     'write as_duration_string undef read as_hours');
+is($item->inty_as_duration_string, undef, 'write as_duration_string undef read as_duration_string');
+
+$item = new_item(inty => 145); $item->inty_as_duration_string('');
+is($item->inty,                    undef, 'write as_duration_string "" read raw');
+is($item->inty_as_minutes,         0,     'write as_duration_string "" read as_minutes');
+is($item->inty_as_hours,           0,     'write as_duration_string "" read as_hours');
+is($item->inty_as_duration_string, undef, 'write as_duration_string "" read as_duration_string');
+
+$item = new_item(inty => 145); $item->inty_as_duration_string("3:21");
+is($item->inty,                    201,    'write as_duration_string 3:21 read raw');
+is($item->inty_as_minutes,         21,     'write as_duration_string 3:21 read as_minutes');
+is($item->inty_as_hours,           3,      'write as_duration_string 3:21 read as_hours');
+is($item->inty_as_duration_string, "3:21", 'write as_duration_string 3:21 read as_duration_string');
+
+$item = new_item(inty => 145); $item->inty_as_duration_string("03:1");
+is($item->inty,                    181,    'write as_duration_string 03:1 read raw');
+is($item->inty_as_minutes,         1,      'write as_duration_string 03:1 read as_minutes');
+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');
+
+# Parametervalidierung
+throws_ok { new_item()->inty_as_duration_string('invalid') } qr/invalid.*format/i, 'invalid duration format';
+
 done_testing();
 done_testing();