1 package SL::DB::RequirementSpec;
 
   6 use Rose::DB::Object::Helpers;
 
   8 use SL::DB::MetaSetup::RequirementSpec;
 
   9 use SL::DB::Manager::RequirementSpec;
 
  10 use SL::Locale::String;
 
  11 use SL::Util qw(_hashify);
 
  13 __PACKAGE__->meta->add_relationship(
 
  15     type           => 'one to many',
 
  16     class          => 'SL::DB::RequirementSpecItem',
 
  17     column_map     => { id => 'requirement_spec_id' },
 
  20     type           => 'one to many',
 
  21     class          => 'SL::DB::RequirementSpecTextBlock',
 
  22     column_map     => { id => 'requirement_spec_id' },
 
  25     type           => 'one to many',
 
  26     class          => 'SL::DB::RequirementSpec',
 
  27     column_map     => { id => 'working_copy_id' },
 
  31 __PACKAGE__->meta->initialize;
 
  33 __PACKAGE__->before_save('_before_save_initialize_not_null_columns');
 
  39   push @errors, t8('The title is missing.') if !$self->title;
 
  44 sub _before_save_initialize_not_null_columns {
 
  47   $self->previous_section_number(0) if !defined $self->previous_section_number;
 
  48   $self->previous_fb_number(0)      if !defined $self->previous_fb_number;
 
  53 sub text_blocks_sorted {
 
  54   my ($self, %params) = _hashify(1, @_);
 
  56   my @text_blocks = @{ $self->text_blocks };
 
  57   @text_blocks    = grep { $_->output_position == $params{output_position} } @text_blocks if exists $params{output_position};
 
  58   @text_blocks    = sort { $a->position        <=> $b->position            } @text_blocks;
 
  60   return wantarray ? @text_blocks : \@text_blocks;
 
  64   my ($self, @rest) = @_;
 
  66   croak "This sub is not a writer" if @rest;
 
  68   my @sections = sort { $a->position <=> $b->position } grep { !$_->parent_id } @{ $self->items };
 
  69   return wantarray ? @sections : \@sections;
 
  72 sub sections { §ions_sorted; }
 
  74 sub displayable_name {
 
  77   return sprintf('%s: "%s"', $self->type->description, $self->title);
 
  80 sub versioned_copies_sorted {
 
  81   my ($self, %params) = _hashify(1, @_);
 
  83   my @copies = @{ $self->versioned_copies };
 
  84   @copies    = grep { $_->version->version_number <=  $params{max_version_number} } @copies if $params{max_version_number};
 
  85   @copies    = sort { $a->version->version_number <=> $b->version->version_number } @copies;
 
  87   return wantarray ? @copies : \@copies;
 
  91   my ($self, %params) = @_;
 
  93   return $self->_create_copy(%params) if $self->db->in_transaction;
 
  96   if (!$self->db->do_transaction(sub { $copy = $self->_create_copy(%params) })) {
 
  97     $::lxdebug->message(LXDebug->WARN(), "create_copy failed: " . join("\n", (split(/\n/, $self->db->error))[0..2]));
 
 105   my ($self, %params) = @_;
 
 107   my $copy = Rose::DB::Object::Helpers::clone_and_reset($self);
 
 108   $copy->copy_from($self, %params);
 
 114   my ($self, $source, %attributes) = @_;
 
 116   croak "Missing parameter 'source'" unless $source;
 
 119   $self->assign_attributes(map({ ($_ => $source->$_) } qw(type_id status_id customer_id project_id title hourly_rate net_sum previous_section_number previous_fb_number is_template)),
 
 123   $self->text_blocks(map { Rose::DB::Object::Helpers::clone_and_reset($_) } @{ $source->text_blocks });
 
 125   # Save new object -- we need its ID for the items.
 
 134     my $cloned = Rose::DB::Object::Helpers::clone_and_reset($item);
 
 135     $cloned->requirement_spec_id($self->id);
 
 136     $cloned->children(map { $clone_item->($_) } @{ $item->children });
 
 138     $id_to_clone{ $item->id } = $cloned;
 
 143   $self->items(map { $clone_item->($_) } @{ $source->sections });
 
 145   # Save the items -- need to do that before setting dependencies.
 
 149   foreach my $item (@{ $source->items }) {
 
 150     next unless @{ $item->dependencies };
 
 151     $id_to_clone{ $item->id }->update_attributes(dependencies => [ map { $id_to_clone{$_->id} } @{ $item->dependencies } ]);
 
 154   $self->update_attributes(%attributes);
 
 159 sub previous_version {
 
 162   my $and    = $self->version_id ? " AND (version_id <> ?)" : "";
 
 163   my $id     = $self->db->dbh->selectrow_array(<<SQL, undef, $self->id, ($self->version_id) x !!$self->version_id);
 
 165    FROM requirement_specs
 
 166    WHERE (working_copy_id = ?) $and
 
 169   return $id ? SL::DB::RequirementSpec->new(id => $id)->load : undef;
 
 172 sub is_working_copy {
 
 175   return !$self->working_copy_id;
 
 178 sub next_version_number {
 
 180   my $max_number = $self->db->dbh->selectrow_array(<<SQL, undef, $self->id);
 
 181     SELECT COALESCE(MAX(ver.version_number), 0)
 
 182     FROM requirement_spec_versions ver
 
 183     JOIN requirement_specs rs ON (rs.version_id = ver.id)
 
 184     WHERE rs.working_copy_id = ?
 
 187   return $max_number + 1;
 
 191   my ($self, %attributes) = @_;
 
 193   croak "Cannot work on a versioned copy" if $self->working_copy_id;
 
 195   my ($copy, $version);
 
 196   my $ok = $self->db->do_transaction(sub {
 
 197     delete $attributes{version_number};
 
 199     $version = SL::DB::RequirementSpecVersion->new(%attributes, version_number => $self->next_version_number)->save;
 
 200     $copy    = $self->create_copy;
 
 201     $copy->update_attributes(version_id => $version->id, working_copy_id => $self->id);
 
 202     $self->update_attributes(version_id => $version->id);
 
 207   return $ok ? ($copy, $version) : ();
 
 210 sub invalidate_version {
 
 211   my ($self, %params) = @_;
 
 213   croak "Cannot work on a versioned copy" if $self->working_copy_id;
 
 215   return if !$self->id || !$self->version_id;
 
 216   $self->update_attributes(version_id => undef);