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);
163 # First convert all items to sections so that deleting won't
164 # violate foreign key constraints on parent_id.
165 SL::DB::Manager::RequirementSpecItem->update_all(
166 set => { parent_id => undef, item_type => 'section' },
168 requirement_spec_id => $self->id,
169 '!parent_id' => undef,
172 # Now delete all items in one go.
173 SL::DB::Manager::RequirementSpecItem->delete_all(where => [ requirement_spec_id => $self->id ]);
175 # Last clear values in ourself.
179 return $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
182 sub previous_version {
185 my $and = $self->version_id ? " AND (version_id <> ?)" : "";
186 my $id = $self->db->dbh->selectrow_array(<<SQL, undef, $self->id, ($self->version_id) x !!$self->version_id);
188 FROM requirement_specs
189 WHERE (working_copy_id = ?) $and
192 return $id ? SL::DB::RequirementSpec->new(id => $id)->load : undef;
195 sub is_working_copy {
198 return !$self->working_copy_id;
201 sub next_version_number {
203 my $max_number = $self->db->dbh->selectrow_array(<<SQL, undef, $self->id);
204 SELECT COALESCE(MAX(ver.version_number), 0)
205 FROM requirement_spec_versions ver
206 JOIN requirement_specs rs ON (rs.version_id = ver.id)
207 WHERE rs.working_copy_id = ?
210 return $max_number + 1;
214 my ($self, %attributes) = @_;
216 croak "Cannot work on a versioned copy" if $self->working_copy_id;
218 my ($copy, $version);
219 my $ok = $self->db->do_transaction(sub {
220 delete $attributes{version_number};
222 $version = SL::DB::RequirementSpecVersion->new(%attributes, version_number => $self->next_version_number)->save;
223 $copy = $self->create_copy;
224 $copy->update_attributes(version_id => $version->id, working_copy_id => $self->id);
225 $self->update_attributes(version_id => $version->id);
230 return $ok ? ($copy, $version) : ();
233 sub invalidate_version {
234 my ($self, %params) = @_;
236 croak "Cannot work on a versioned copy" if $self->working_copy_id;
238 return if !$self->id || !$self->version_id;
239 $self->update_attributes(version_id => undef);