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;
12 __PACKAGE__->meta->add_relationship(
14 type => 'one to many',
15 class => 'SL::DB::RequirementSpecItem',
16 column_map => { id => 'requirement_spec_id' },
19 type => 'one to many',
20 class => 'SL::DB::RequirementSpecTextBlock',
21 column_map => { id => 'requirement_spec_id' },
24 type => 'one to many',
25 class => 'SL::DB::RequirementSpec',
26 column_map => { id => 'working_copy_id' },
30 __PACKAGE__->meta->initialize;
32 __PACKAGE__->before_save('_before_save_initialize_not_null_columns');
38 push @errors, t8('The title is missing.') if !$self->title;
43 sub _before_save_initialize_not_null_columns {
46 $self->previous_section_number(0) if !defined $self->previous_section_number;
47 $self->previous_fb_number(0) if !defined $self->previous_fb_number;
52 sub text_blocks_for_position {
53 my ($self, $output_position) = @_;
55 return [ sort { $a->position <=> $b->position } grep { $_->output_position == $output_position } @{ $self->text_blocks } ];
59 my ($self, @rest) = @_;
61 croak "This sub is not a writer" if @rest;
63 return [ sort { $a->position <=> $b->position } grep { !$_->parent_id } @{ $self->items } ];
66 sub displayable_name {
69 return sprintf('%s: "%s"', $self->type->description, $self->title);
73 my ($self, %params) = @_;
75 return $self->_create_copy(%params) if $self->db->in_transaction;
78 if (!$self->db->do_transaction(sub { $copy = $self->_create_copy(%params) })) {
79 $::lxdebug->message(LXDebug->WARN(), "create_copy failed: " . join("\n", (split(/\n/, $self->db->error))[0..2]));
87 my ($self, %params) = @_;
89 my $copy = Rose::DB::Object::Helpers::clone_and_reset($self);
90 $copy->copy_from($self, %params);
96 my ($self, $source, %attributes) = @_;
98 croak "Missing parameter 'source'" unless $source;
101 $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)),
105 $self->text_blocks(map { Rose::DB::Object::Helpers::clone_and_reset($_) } @{ $source->text_blocks });
107 # Save new object -- we need its ID for the items.
116 my $cloned = Rose::DB::Object::Helpers::clone_and_reset($item);
117 $cloned->requirement_spec_id($self->id);
118 $cloned->children(map { $clone_item->($_) } @{ $item->children });
120 $id_to_clone{ $item->id } = $cloned;
125 $self->items(map { $clone_item->($_) } @{ $source->sections });
127 # Save the items -- need to do that before setting dependencies.
131 foreach my $item (@{ $source->items }) {
132 next unless @{ $item->dependencies };
133 $id_to_clone{ $item->id }->update_attributes(dependencies => [ map { $id_to_clone{$_->id} } @{ $item->dependencies } ]);
136 $self->update_attributes(%attributes);
145 # First convert all items to sections so that deleting won't
146 # violate foreign key constraints on parent_id.
147 SL::DB::Manager::RequirementSpecItem->update_all(
148 set => { parent_id => undef, item_type => 'section' },
150 requirement_spec_id => $self->id,
151 '!parent_id' => undef,
154 # Now delete all items in one go.
155 SL::DB::Manager::RequirementSpecItem->delete_all(where => [ requirement_spec_id => $self->id ]);
157 # Last clear values in ourself.
161 return $self->db->in_transaction ? $worker->() : $self->db->do_transaction($worker);
164 sub previous_version {
167 my $and = $self->version_id ? " AND (version_id <> ?)" : "";
168 my $id = $self->db->dbh->selectrow_array(<<SQL, undef, $self->id, ($self->version_id) x !!$self->version_id);
170 FROM requirement_specs
171 WHERE (working_copy_id = ?) $and
174 return $id ? SL::DB::RequirementSpec->new(id => $id)->load : undef;
177 sub is_working_copy {
180 return !$self->working_copy_id;
183 sub next_version_number {
185 my $max_number = $self->db->dbh->selectrow_array(<<SQL, undef, $self->id);
186 SELECT COALESCE(MAX(ver.version_number), 0)
187 FROM requirement_spec_versions ver
188 JOIN requirement_specs rs ON (rs.version_id = ver.id)
189 WHERE rs.working_copy_id = ?
192 return $max_number + 1;
196 my ($self, %attributes) = @_;
198 my ($copy, $version);
199 my $ok = $self->db->do_transaction(sub {
200 delete $attributes{version_number};
202 $version = SL::DB::RequirementSpecVersion->new(%attributes, version_number => $self->next_version_number)->save;
203 $copy = $self->create_copy;
204 $copy->update_attributes(version_id => $version->id, working_copy_id => $self->id);
205 $self->update_attributes(version_id => $version->id);
210 return $ok ? ($copy, $version) : ();
213 sub invalidate_version {
214 my ($self, %params) = @_;
216 $::lxdebug->message(0, "Invalidate version called for id " . $self->id . " version " . $self->version_id);
217 $::lxdebug->show_backtrace(1);
219 return if !$self->id || !$self->version_id;
220 $self->update_attributes(version_id => undef);