X-Git-Url: http://wagnertech.de/gitweb/gitweb.cgi/mfinanz.git/blobdiff_plain/48aa4607a8845148f7f7be6a4bf990a798abb566..51fec3106e49e1ffc7d6a0a2c577bea8641efbff:/SL/Controller/RequirementSpecVersion.pm diff --git a/SL/Controller/RequirementSpecVersion.pm b/SL/Controller/RequirementSpecVersion.pm new file mode 100644 index 000000000..058884709 --- /dev/null +++ b/SL/Controller/RequirementSpecVersion.pm @@ -0,0 +1,183 @@ +package SL::Controller::RequirementSpecVersion; + +use strict; + +use parent qw(SL::Controller::Base); + +use Carp; +use List::MoreUtils qw(any); + +use SL::ClientJS; +use SL::DB::Customer; +use SL::DB::Project; +use SL::DB::RequirementSpec; +use SL::DB::RequirementSpecVersion; +use SL::Helper::Flash; +use SL::Locale::String; + +use Rose::Object::MakeMethods::Generic +( + 'scalar --get_set_init' => [ qw(requirement_spec version js versioned_copies) ], +); + +__PACKAGE__->run_before('check_auth'); + +# +# actions +# + +sub action_list { + my ($self, %params) = @_; + + $self->render('requirement_spec_version/list', { layout => 0 }); +} + +sub action_new { + my ($self) = @_; + + $self->version(SL::DB::RequirementSpecVersion->new); + + my $previous_version = $self->requirement_spec->previous_version; + my %differences = $self->calculate_differences(current => $self->requirement_spec, previous => $previous_version); + + if (!$previous_version) { + $self->version->description(t8('Initial version.')); + + } else { + my @lines; + + my $fb_diff = $differences{function_blocks}; + push @lines, t8('Added sections and function blocks: #1', $::locale->language_join([ map { $_->fb_number } @{ $fb_diff->{additions} } ])) if @{ $fb_diff->{additions} }; + push @lines, t8('Changed sections and function blocks: #1', $::locale->language_join([ map { $_->fb_number } @{ $fb_diff->{changes} } ])) if @{ $fb_diff->{changes} }; + push @lines, t8('Removed sections and function blocks: #1', $::locale->language_join([ map { $_->fb_number } @{ $fb_diff->{removals} } ])) if @{ $fb_diff->{removals} }; + + my $tb_diff = $differences{text_blocks}; + push @lines, t8('Added text blocks: #1', $::locale->language_join([ map { '"' . $_->title . '"' } @{ $tb_diff->{additions} } ])) if @{ $tb_diff->{additions} }; + push @lines, t8('Changed text blocks: #1', $::locale->language_join([ map { '"' . $_->title . '"' } @{ $tb_diff->{changes} } ])) if @{ $tb_diff->{changes} }; + push @lines, t8('Removed text blocks: #1', $::locale->language_join([ map { '"' . $_->title . '"' } @{ $tb_diff->{removals} } ])) if @{ $tb_diff->{removals} }; + + $self->version->description(@lines ? join("\n", @lines) : t8('No changes since previous version.')); + } + + $self->render('requirement_spec_version/new', { layout => 0 }, title => t8('Create a new version')); +} + +sub action_create { + my ($self, %params) = @_; + + my %attributes = %{ delete($::form->{rs_version}) || {} }; + my @errors = SL::DB::RequirementSpecVersion->new(%attributes, version_number => 1)->validate; + + return $self->js->error(@errors)->render($self) if @errors; + + my $db = $self->requirement_spec->db; + my @result = $self->version($self->requirement_spec->create_version(%attributes)); + + if (!@result) { + $::lxdebug->message(LXDebug::WARN(), "Error: " . $db->error); + return $self->js->error($::locale->text('Saving failed. Error message from the database: #1'), $db->error)->render($self); + } + + my $html = $self->render('requirement_spec/_version', { output => 0 }, requirement_spec => $self->requirement_spec); + + $self->js + ->html('#requirement_spec_version', $html) + ->jqmClose('.jqmWindow') + ->render($self); +} + +# +# filters +# + +sub check_auth { + my ($self, %params) = @_; + $::auth->assert('sales_quotation_edit'); +} + +# +# helpers +# + +sub init_requirement_spec { + my ($self) = @_; + $self->requirement_spec(SL::DB::RequirementSpec->new(id => $::form->{requirement_spec_id})->load) if $::form->{requirement_spec_id}; +} + +sub init_version { + my ($self) = @_; + $self->version(SL::DB::RequirementSpecVersion->new(id => $::form->{id})->load) if $::form->{id}; +} + +sub init_js { + my ($self, %params) = @_; + $self->js(SL::ClientJS->new); +} + +sub init_versioned_copies { + my ($self) = @_; + $self->versioned_copies([ + sort { $b->mtime <=> $a->mtime } @{ $self->requirement_spec->versioned_copies } + ]); +} + +sub has_item_changed { + my ($previous, $current) = @_; + croak "Missing previous/current" if !$previous || !$current; + return any { ($previous->$_ || '') ne ($current->$_ || '') } qw(item_type parent_id fb_number title description complexity_id risk_id time_estimation net_sum); +} + +sub has_text_block_changed { + my ($previous, $current) = @_; + croak "Missing previous/current" if !$previous || !$current; + return any { ($previous->$_ || '') ne ($current->$_ || '') } qw(title text); +} + +sub compare_items { + return -1 if ($a->item_type eq 'section') && ($b->item_type ne 'section'); + return +1 if ($a->item_type ne 'section') && ($b->item_type eq 'section'); + return $a->fb_number cmp $b->fb_number; +} + +sub calculate_differences { + my ($self, %params) = @_; + + my %differences = ( + function_blocks => { + additions => [], + changes => [], + removals => [], + }, + text_blocks => { + additions => [], + changes => [], + removals => [], + }, + ); + + return %differences if !$params{previous} || !$params{current}; + + my @previous_items = sort compare_items @{ $params{previous}->items }; + my @current_items = sort compare_items @{ $params{current}->items }; + + my @previous_text_blocks = sort { lc $a->title cmp lc $b->title } @{ $params{previous}->text_blocks }; + my @current_text_blocks = sort { lc $a->title cmp lc $b->title } @{ $params{current}->text_blocks }; + + my %previous_items_map = map { $_->fb_number => $_ } @previous_items; + my %current_items_map = map { $_->fb_number => $_ } @current_items; + + my %previous_text_blocks_map = map { $_->title => $_ } @previous_text_blocks; + my %current_text_blocks_map = map { $_->title => $_ } @current_text_blocks; + + $differences{function_blocks}->{additions} = [ grep { !$previous_items_map{ $_->fb_number } } @current_items ]; + $differences{function_blocks}->{removals} = [ grep { !$current_items_map{ $_->fb_number } } @previous_items ]; + $differences{function_blocks}->{changes} = [ grep { $previous_items_map{ $_->fb_number } && has_item_changed($previous_items_map{ $_->fb_number }, $_) } @current_items ]; + + $differences{text_blocks}->{additions} = [ grep { !$previous_text_blocks_map{ $_->title } } @current_text_blocks ]; + $differences{text_blocks}->{removals} = [ grep { !$current_text_blocks_map{ $_->title } } @previous_text_blocks ]; + $differences{text_blocks}->{changes} = [ grep { $previous_text_blocks_map{ $_->title } && has_text_block_changed($previous_text_blocks_map{ $_->title }, $_) } @current_text_blocks ]; + + return %differences; +} + +1;