Pflichtenheftaufträge: beliebige Artikel auswählen können
authorMoritz Bunkus <m.bunkus@linet-services.de>
Mon, 25 Aug 2014 13:25:47 +0000 (15:25 +0200)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Tue, 26 Aug 2014 12:17:56 +0000 (14:17 +0200)
Zusätzlich werden dann Spalten angezeigt, die die Einheit und den im
Angebot/Auftrag verwendeten Positionstypen (Pauschalposition/
Auwandsposition) angeben.

SL/Controller/RequirementSpec.pm
SL/Controller/RequirementSpecOrder.pm
SL/DB/Manager/Unit.pm
SL/DB/Unit.pm
js/locale/de.js
js/requirement_spec.js
locale/de/all
templates/webpages/requirement_spec/show.html
templates/webpages/requirement_spec_order/_assignment_form.html
templates/webpages/requirement_spec_order/list.html

index a29c7cb..f762b97 100644 (file)
@@ -29,7 +29,7 @@ use Rose::Object::MakeMethods::Generic
 (
   scalar                  => [ qw(requirement_spec_item visible_item visible_section) ],
   'scalar --get_set_init' => [ qw(requirement_spec customers types statuses complexities risks projects project_types project_statuses default_project_type default_project_status copy_source js
-                                  current_text_block_output_position models) ],
+                                  current_text_block_output_position models time_based_units) ],
 );
 
 __PACKAGE__->run_before('setup');
@@ -289,6 +289,7 @@ sub init_project_types          { SL::DB::Manager::ProjectType->get_all_sorted
 sub init_projects               { SL::DB::Manager::Project->get_all_sorted                    }
 sub init_risks                  { SL::DB::Manager::RequirementSpecRisk->get_all_sorted        }
 sub init_statuses               { SL::DB::Manager::RequirementSpecStatus->get_all_sorted      }
+sub init_time_based_units       { SL::DB::Manager::Unit->time_based_units                     }
 sub init_types                  { SL::DB::Manager::RequirementSpecType->get_all_sorted        }
 
 sub init_customers {
index b02c985..f354708 100644 (file)
@@ -23,7 +23,7 @@ use constant FORMS_SELECTOR => '#quotations_and_orders_article_assignment,#quota
 use Rose::Object::MakeMethods::Generic
 (
   scalar                  => [ qw(parts) ],
-  'scalar --get_set_init' => [ qw(requirement_spec rs_order js h_unit_name all_customers all_parts_time_unit) ],
+  'scalar --get_set_init' => [ qw(requirement_spec rs_order js h_unit_name all_customers all_parts_time_unit section_order_part) ],
 );
 
 __PACKAGE__->run_before('setup');
@@ -156,6 +156,7 @@ sub action_edit_assignment {
   my $html   = $self->render('requirement_spec_order/edit_assignment', { output => 0 }, make_part_title => sub { $_[0]->partnumber . ' ' . $_[0]->description });
   $self->js->hide(LIST_SELECTOR())
            ->after(LIST_SELECTOR(), $html)
+           ->reinit_widgets
            ->render($self);
 }
 
@@ -209,15 +210,16 @@ sub init_js {
 }
 
 sub init_all_customers { SL::DB::Manager::Customer->get_all_sorted }
-sub init_h_unit_name   { first { SL::DB::Manager::Unit->find_by(name => $_) } qw(Std h Stunde) };
+sub init_h_unit_name   { SL::DB::Manager::Unit->find_h_unit->name };
 sub init_rs_order      { SL::DB::RequirementSpecOrder->new(id => $::form->{rs_order_id})->load };
+sub init_section_order_part { my $id = $::instance_conf->get_requirement_spec_section_order_part_id; return $id ? SL::DB::Part->new(id => $id)->load : undef }
 
 sub init_all_parts_time_unit {
   my ($self) = @_;
 
   return [] unless $self->h_unit_name;
 
-  my @convertible_unit_names = map { $_->name } @{ SL::DB::Manager::Unit->find_by(name => $self->h_unit_name)->convertible_units };
+  my @convertible_unit_names = map { $_->name } @{ SL::DB::Manager::Unit->time_based_units };
 
   return SL::DB::Manager::Part->get_all_sorted(where => [ unit => \@convertible_unit_names ]);
 }
index 673fcb0..aa55ffa 100644 (file)
@@ -8,6 +8,8 @@ use base qw(SL::DB::Helper::Manager);
 use SL::DB::Helper::Sorted;
 use SL::DB::Helper::Filtered;
 
+use List::Util qw(first);
+
 sub object_class { 'SL::DB::Unit' }
 
 __PACKAGE__->make_manager_methods;
@@ -46,4 +48,18 @@ sub all_units {
   $::request->cache('all_units')->{sorted} //= $class->get_all_sorted;
 }
 
+sub find_h_unit {
+  my ($class) = @_;
+
+  return $::request->cache('unit_manager')->{h_unit} //= first { $_->name =~ m{^(?: Std | h | Stunde )$}x } @{ $class->all_units };
+}
+
+sub time_based_units {
+  my ($class) = @_;
+
+  my $h_unit = $class->find_h_unit;
+  return [] if !$h_unit;
+  return $::request->cache('unit_manager')->{units} //= $h_unit->convertible_units;
+}
+
 1;
index 900558f..f5f1bc1 100644 (file)
@@ -1,5 +1,8 @@
 package SL::DB::Unit;
 
+use List::MoreUtils qw(any);
+
+
 use strict;
 
 use SL::DB::MetaSetup::Unit;
@@ -57,4 +60,10 @@ sub convert_to {
   return $qty * $my_base_factor / $other_base_factor;
 }
 
+sub is_time_based {
+  my ($self) = @_;
+
+  return any { $_->id == $self->id } @{ SL::DB::Manager::Unit->time_based_units };
+}
+
 1;
index e00362d..a382fd9 100644 (file)
@@ -70,5 +70,7 @@ namespace("kivi").setupLocale({
 "Transaction description":"Vorgangsbezeichnung",
 "Update":"Erneuern",
 "Update quotation/order":"Auftrag/Angebot aktualisieren",
-"Version actions":"Aktionen für Versionen"
+"Version actions":"Aktionen für Versionen",
+"flat-rate position":"Pauschalposition",
+"time and effort based position":"Aufwandsposition"
 });
index b1d5ce8..d0ec560 100644 (file)
@@ -153,6 +153,8 @@ ns.initialize_requirement_spec = function(data) {
 
   ns.create_context_menus(data.is_template);
   $('#requirement_spec_tabs').on("tabsbeforeactivate", ns.tabs_before_activate);
+
+  ns.time_based_units = data.time_based_units;
 };
 
 // -------------------------------------------------------------------------
@@ -565,6 +567,31 @@ ns.assign_order_part_id_to_all = function() {
   }).each(function(idx, elt) {
     $(elt).val(order_part_name);
   });
+
+  var unit = $('#quotations_and_orders_order_id').closest('td').data('unit');
+  var text = ns.time_based_units[unit] ? kivi.t8("time and effort based position") : kivi.t8("flat-rate position");
+
+  $('#quotations_and_orders_form [data-unit-column=1]').html(unit);
+  $('#quotations_and_orders_form [data-position-type-column=1]').html(text);
+};
+
+ns.assign_order_part_on_part_picked = function(event, item) {
+  if (!item || !item.unit)
+    return;
+
+  var $elt = $(this),
+      id   = $elt.prop('id');
+
+  if (id == 'quotations_and_orders_order_id')
+    $elt.closest('td').data('unit', item.unit);
+
+  else {
+    var $tr  = $elt.closest('tr');
+    var text = ns.time_based_units[item.unit] ? kivi.t8("time and effort based position") : kivi.t8("flat-rate position");
+
+    $tr.find('[data-unit-column=1]').html(item.unit);
+    $tr.find('[data-position-type-column=1]').html(text);
+  }
 };
 
 // -------------------------------------------------------------------------
@@ -1037,3 +1064,9 @@ ns.create_context_menus = function(is_template) {
 };
 
 });                             // end of namespace(...., function() {...
+
+function local_reinit_widgets() {
+  kivi.run_once_for('#quotations_and_orders_order_id,[name="sections[].order_part_id"]', "assign_order_part_on_part_picked", function(elt) {
+    $(elt).on('set_item:PartPicker', kivi.requirement_spec.assign_order_part_on_part_picked);
+  });
+}
index 05c8018..98e54f6 100755 (executable)
@@ -1791,6 +1791,7 @@ $self->{texts} = {
   'Plural'                      => 'Plural',
   'Port'                        => 'Port',
   'Portrait'                    => 'Hochformat',
+  'Position type in quotation/order' => 'Positionstyp in Angebot/Auftrag',
   'Post'                        => 'Buchen',
   'Post Payment'                => 'Zahlung buchen',
   'Post and E-mail'             => 'Buchen und E-Mail',
@@ -2976,6 +2977,7 @@ $self->{texts} = {
   'executed'                    => 'ausgeführt',
   'failed'                      => 'fehlgeschlagen',
   'female'                      => 'weiblich',
+  'flat-rate position'          => 'Pauschalposition',
   'follow_up_list'              => 'wiedervorlageliste',
   'for'                         => 'f&uuml;r',
   'for Period'                  => 'für den Zeitraum',
@@ -3102,6 +3104,7 @@ $self->{texts} = {
   'taxkey 0 with taxrate 0 was created.' => 'Steuerschlüssel 0 wurde angelegt.',
   'taxnumber'                   => 'Automatikkonto',
   'terminated'                  => 'gekündigt',
+  'time and effort based position' => 'Aufwandsposition',
   'to (date)'                   => 'bis',
   'to (time)'                   => 'bis',
   'transfer'                    => 'Umlagerung',
index 756fbc8..c329a4e 100644 (file)
@@ -1,4 +1,4 @@
-[%- USE JSON -%][%- USE HTML %][%- USE L %][%- USE LxERP %][%- USE P -%]
+[%- USE JSON -%][%- USE HTML %][%- USE L %][%- USE LxERP %][%- USE P -%][%- USE JavaScript -%]
 [% SET sections = SELF.requirement_spec.sections_sorted || [] %]
 
 [%- INCLUDE 'common/flash.html' %]
@@ -95,6 +95,11 @@ $(function() {
 [% IF SELF.requirement_spec_item %]
     , initially_selected_node: '#fb-[% SELF.requirement_spec_item.id %]'
 [% END %]
+    , time_based_units: {
+      [% FOREACH unit = SELF.time_based_units %]
+       [% UNLESS loop.first %], [% END %] "[% JavaScript.escape(unit.name) %]": true
+      [% END %]
+    }
   });
 });
 
index 9990c55..634cb24 100644 (file)
@@ -17,8 +17,8 @@
 
   <tr>
    <td>[% LxERP.t8("Assign the following article to all sections") %]:</td>
-   <td>
-    [% P.part_picker('quotations_and_orders_dummy', INSTANCE_CONF.get_requirement_spec_section_order_part_id, convertible_unit=SELF.h_unit_name, id='quotations_and_orders_order_id', style=style) %]
+   <td data-unit="[% HTML.escape(SELF.section_order_part.unit) %]">
+    [% P.part_picker('quotations_and_orders_dummy', SELF.section_order_part.id, id='quotations_and_orders_order_id', style=style) %]
     [% L.button_tag('kivi.requirement_spec.assign_order_part_id_to_all()', LxERP.t8('Assign article')) %]
    </td>
   </tr>
@@ -31,6 +31,8 @@
     <th>[% LxERP.t8("Title") %]</th>
     <th>[% LxERP.t8("Description") %]</th>
     <th>[% LxERP.t8("Article") %]</th>
+    <th>[% LxERP.t8("Unit") %]</th>
+    <th>[% LxERP.t8("Position type in quotation/order") %]</th>
    </tr>
   </thead>
 
     <td>[% HTML.escape(section.fb_number) %]</td>
     <td>[% HTML.escape(section.title) %]</td>
     <td>[% HTML.escape(P.truncate(section.description_as_stripped_html)) %]</td>
-    <td>[% P.part_picker('sections[].order_part_id', section.order_part_id, convertible_unit=SELF.h_unit_name, id='quotations_and_orders_sections_order_pard_id_' _ loop.count, style=style) %]</td>
+    <td>[% P.part_picker('sections[].order_part_id', section.order_part_id, id='quotations_and_orders_sections_order_pard_id_' _ loop.count, style=style) %]</td>
+    <td data-unit-column=1>[% HTML.escape(section.order_part.unit) %]</td>
+    <td data-position-type-column=1>
+     [% IF section.order_part_id && section.order_part.unit_obj.is_time_based %]
+      [% LxERP.t8("time and effort based position") %]
+     [% ELSIF section.order_part_id %]
+      [% LxERP.t8("flat-rate position") %]
+     [% END %]
+    </td>
    </tr>
    [% END %]
   </tbody>
index b4bef1d..4f8278d 100644 (file)
@@ -15,6 +15,8 @@
     <th>[% LxERP.t8("Title") %]</th>
     <th>[% LxERP.t8("Description") %]</th>
     <th>[% LxERP.t8("Article") %]</th>
+    <th>[% LxERP.t8("Unit") %]</th>
+    <th>[% LxERP.t8("Position type in quotation/order") %]</th>
    </tr>
   </thead>
 
        [% LxERP.t8("no article assigned yet") %]
       [% END %]
      </td>
+     <td>[% HTML.escape(section.order_part.unit) %]</td>
+     <td>
+      [% IF section.order_part_id && section.order_part.unit_obj.is_time_based %]
+       [% LxERP.t8("time and effort based position") %]
+      [% ELSIF section.order_part_id %]
+       [% LxERP.t8("flat-rate position") %]
+      [% END %]
+     </td>
     </tr>
    [% END %]
   </tbody>