1 package SL::DB::Helper::AttrDuration;
 
   5 use parent qw(Exporter);
 
   6 our @EXPORT = qw(attr_duration attr_duration_minutes);
 
  11   my ($package, @attributes) = @_;
 
  13   _make($package, $_) for @attributes;
 
  16 sub attr_duration_minutes {
 
  17   my ($package, @attributes) = @_;
 
  19   _make_minutes($package, $_) for @attributes;
 
  23   my ($package, $attribute) = @_;
 
  27   *{ $package . '::' . $attribute . '_as_hours' } = sub {
 
  28     my ($self, $value) = @_;
 
  30     $self->$attribute(int($value) + ($self->$attribute - int($self->$attribute))) if @_ > 1;
 
  31     return int($self->$attribute // 0);
 
  34   *{ $package . '::' . $attribute . '_as_minutes' } = sub {
 
  35     my ($self, $value) = @_;
 
  37     $self->$attribute(int($self->$attribute) * 1.0 + ($value // 0) / 60.0) if @_ > 1;
 
  38     return int(($self->$attribute // 0) * 60.0 + 0.5) % 60;
 
  41   *{ $package . '::' . $attribute . '_as_duration_string' } = sub {
 
  42     my ($self, $value) = @_;
 
  44     $self->$attribute(defined($value) ? $::form->parse_amount(\%::myconfig, $value) * 1 : undef) if @_ > 1;
 
  45     return defined($self->$attribute) ? $::form->format_amount(\%::myconfig, $self->$attribute // 0, 2) : undef;
 
  48   *{ $package . '::' . $attribute . '_as_man_days' } = sub {
 
  49     my ($self, $value) = @_;
 
  52       return undef if !defined $value;
 
  53       $self->$attribute($value);
 
  55     $value = $self->$attribute // 0;
 
  56     return $value >= 8.0 ? $value / 8.0 : $value;
 
  59   *{ $package . '::' . $attribute . '_as_man_days_unit' } = sub {
 
  60     my ($self, $unit) = @_;
 
  63       return undef if !defined $unit;
 
  64       croak "Unknown unit '${unit}'"                    if $unit !~ m/^(?:h|hour|man_day)$/;
 
  65       $self->$attribute(($self->$attribute // 0) * 8.0) if $unit eq 'man_day';
 
  68     return ($self->$attribute // 0) >= 8.0 ? 'man_day' : 'h'
 
  71   *{ $package . '::' . $attribute . '_as_man_days_string' } = sub {
 
  72     my ($self, $value) = @_;
 
  73     my $method         = "${attribute}_as_man_days";
 
  76       return undef if !defined $value;
 
  77       $self->$method($::form->parse_amount(\%::myconfig, $value));
 
  80     return $::form->format_amount(\%::myconfig, $self->$method // 0, 2);
 
  85   my ($package, $attribute) = @_;
 
  89   *{ $package . '::' . $attribute . '_as_hours' } = sub {
 
  90     my ($self, $value) = @_;
 
  92     $self->$attribute($value * 60 + ($self->$attribute % 60)) if @_ > 1;
 
  93     return int(($self->$attribute // 0) / 60);
 
  96   *{ $package . '::' . $attribute . '_as_minutes' } = sub {
 
  97     my ($self, $value) = @_;
 
  99     $self->$attribute(int($self->$attribute) - (int($self->$attribute) % 60) + ($value // 0)) if @_ > 1;
 
 100     return ($self->$attribute // 0) % 60;
 
 103   *{ $package . '::' . $attribute . '_as_duration_string' } = sub {
 
 104     my ($self, $value) = @_;
 
 107       if (!defined($value) || ($value eq '')) {
 
 108         $self->$attribute(undef);
 
 110         croak $::locale->text("Invalid duration format") if $value !~ m{^(?:(\d*):)?(\d+)$};
 
 111         $self->$attribute(($1 // 0) * 60 + ($2 // 0));
 
 115     my $as_hours   = "${attribute}_as_hours";
 
 116     my $as_minutes = "${attribute}_as_minutes";
 
 117     return defined($self->$attribute) ? sprintf('%d:%02d', $self->$as_hours, $self->$as_minutes) : undef;
 
 120   *{ $package . '::' . $attribute . '_in_hours' } = sub {
 
 121     my ($self, $value) = @_;
 
 123     $self->$attribute(int($value * 60 + 0.5)) if @_ > 1;
 
 124     return $self->$attribute / 60.0;
 
 127   *{ $package . '::' . $attribute . '_in_hours_as_number' } = sub {
 
 128     my ($self, $value) = @_;
 
 130     my $sub = "${attribute}_in_hours";
 
 132     $self->$sub($::form->parse_amount(\%::myconfig, $value)) if @_ > 1;
 
 133     return $::form->format_amount(\%::myconfig, $self->$sub, 2);
 
 146 SL::DB::Helper::AttrDuration - Attribute helper for duration stored in
 
 152   use SL::DB::Helper::AttrDuration;
 
 153   __PACKAGE__->attr_duration('time_estimation');
 
 154   __PACKAGE__->attr_duration_minutes('hours');
 
 157   print "Minutes: " . $obj->time_estimation_as_minutes . " hours: " . $obj->time_estimation_as_hours . "\n";
 
 159   # Use formatted strings in input fields in templates:
 
 162     [% L.input_tag('time_estimation_as_duration_string', SELF.obj.time_estimation_as_duration_string) %]
 
 167 This is a helper for columns that store a duration in one of two formats:
 
 171 =item 1. as a numeric or floating point number representing a number
 
 174 =item 2. as an integer presenting a number of minutes
 
 178 In the first case the value 1.75 would stand for "1 hour, 45
 
 179 minutes". In the second case the value 105 represents the same
 
 182 The helper methods created depend on the mode. Calling
 
 183 C<attr_duration> makes the following methods available:
 
 187 =item C<attribute_as_minutes [$new_value]>
 
 189 Access only the minutes. Return values are in the range [0 - 59].
 
 191 =item C<attribute_as_hours [$new_value]>
 
 193 Access only the hours. Returns an integer value.
 
 195 =item C<attribute_as_duration_string [$new_value]>
 
 197 Access the full value as a formatted string according to the user's
 
 200 =item C<attribute_as_man_days [$new_value]>
 
 202 Access the attribute as a number of man days which are assumed to be 8
 
 203 hours long. If the underlying attribute is less than 8 then the value
 
 204 itself will be returned. Otherwise the value divided by 8 is returned.
 
 206 If used as a setter then the underlying attribute is simply set to
 
 207  C<$new_value>. Intentional use is to set the man days first and the
 
 210   $obj->attribute_as_man_days($::form->{attribute_as_man_days});
 
 211   $obj->attribute_as_man_days_unit($::form->{attribute_as_man_days_unit});
 
 213 Note that L<SL::DB::Object/assign_attributes> is aware of this and
 
 214 handles this case correctly.
 
 216 =item C<attribute_as_man_days_unit [$new_unit]>
 
 218 Returns the unit that the number returned by L</attribute_as_man_days>
 
 219 represents. This can be either C<h> if the underlying attribute is
 
 220 less than 8 and C<man_day> otherwise.
 
 222 If used as a setter then the underlying attribute is multiplied by 8
 
 223 if C<$new_unit> equals C<man_day>. Otherwise the underlying attribute
 
 224 is not modified. Intentional use is to set the man days first and the
 
 227   $obj->attribute_as_man_days($::form->{attribute_as_man_days});
 
 228   $obj->attribute_as_man_days_unit($::form->{attribute_as_man_days_unit});
 
 230 Note that L<SL::DB::Object/assign_attributes> is aware of this and
 
 231 handles this case correctly.
 
 235 With C<attr_duration_minutes> the following methods are available:
 
 239 =item C<attribute_as_minutes [$new_value]>
 
 241 Access only the minutes. Return values are in the range [0 - 59].
 
 243 =item C<attribute_as_hours [$new_value]>
 
 245 Access only the hours. Returns an integer value.
 
 247 =item C<attribute_as_duration_string [$new_value]>
 
 249 Access the full value as a formatted string in the form C<h:mm>,
 
 250 e.g. C<1:30> for the value 90 minutes. Parsing such a string is
 
 253 =item C<attribute_in_hours [$new_value]>
 
 255 Access the full value but convert to and from hours when
 
 256 reading/writing the value.
 
 258 =item C<attribute_in_hours_as_number [$new_value]>
 
 260 Access the full value but convert to and from hours when
 
 261 reading/writing the value. The value is formatted to/parsed from the
 
 262 user's number format.
 
 270 =item C<attr_duration @attributes>
 
 272 Package method. Call with the names of attributes for which the helper
 
 273 methods should be created.
 
 275 =item C<attr_duration_minutes @attributes>
 
 277 Package method. Call with the names of attributes for which the helper
 
 278 methods should be created.
 
 288 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>