Merge branch 'master' of github.com:kivitendo/kivitendo-erp
[kivitendo-erp.git] / SL / DB / Object.pm
index d1e6cb0..8b86e97 100755 (executable)
@@ -60,6 +60,15 @@ sub _assign_attributes {
 
   my %types      = map { $_->name => $_->type } ref($self)->meta->columns;
 
+  # Special case for *_as_man_days / *_as_man_days_string /
+  # *_as_man_days_unit: the _unit variation must always be called
+  # after the non-unit methods.
+  my @man_days_attributes = grep { m/_as_man_days(?:_string)?$/ } keys %attributes;
+  foreach my $attribute (@man_days_attributes) {
+    my $value = delete $attributes{$attribute};
+    $self->$attribute(defined($value) && ($value eq '') ? undef : $value);
+  }
+
   while (my ($attribute, $value) = each %attributes) {
     my $type = lc($types{$attribute} || 'text');
     $value   = $type eq 'boolean'                ? ($value ? 't' : 'f')
@@ -96,6 +105,17 @@ sub call_sub_if {
   return $check ? $self->$sub(@_) : $self;
 }
 
+sub get_first_conflicting {
+  my ($self, @attributes) = @_;
+
+  my $primary_key         = ($self->meta->primary_key)[0];
+  my @where               = map { ($_ => $self->$_) } @attributes;
+
+  push @where, ("!$primary_key" => $self->$primary_key) if $self->$primary_key;
+
+  return $self->_get_manager_class->get_first(where => [ and => \@where ]);
+}
+
 # These three functions cannot sit in SL::DB::Object::Hooks because
 # mixins don't deal well with super classes (SUPER is the current
 # package's super class, not $self's).
@@ -114,12 +134,14 @@ sub save {
 
   my ($result, $exception);
   my $worker = sub {
-    SL::DB::Object::Hooks::run_hooks($self, 'before_save');
     $exception = $EVAL_ERROR unless eval {
+      SL::DB::Object::Hooks::run_hooks($self, 'before_save');
       $result = $self->SUPER::save(@args);
+      SL::DB::Object::Hooks::run_hooks($self, 'after_save', $result);
       1;
     };
-    SL::DB::Object::Hooks::run_hooks($self, 'after_save', $result);
+
+    return $result;
   };
 
   $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
@@ -134,12 +156,14 @@ sub delete {
 
   my ($result, $exception);
   my $worker = sub {
-    SL::DB::Object::Hooks::run_hooks($self, 'before_delete');
     $exception = $EVAL_ERROR unless eval {
+      SL::DB::Object::Hooks::run_hooks($self, 'before_delete');
       $result = $self->SUPER::delete(@args);
+      SL::DB::Object::Hooks::run_hooks($self, 'after_delete', $result);
       1;
     };
-    SL::DB::Object::Hooks::run_hooks($self, 'after_delete', $result);
+
+    return $result;
   };
 
   $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
@@ -220,6 +244,13 @@ whether or not C<$name> is called.
 Returns the sub's result if the check is positive and C<$self>
 otherwise.
 
+=item C<get_first_conflicting @attributes>
+
+Returns the first object for which all properties listed in
+C<@attributes> equal those in C<$self> but which is not C<$self>. Can
+be used to check whether or not an object's columns are unique before
+saving or during validation.
+
 =back
 
 =head1 AUTHOR