]> wagnertech.de Git - kivitendo-erp.git/commitdiff
Pflichtenhefte: zusätzliche Artikel zuweisen und bearbeiten können
authorMoritz Bunkus <m.bunkus@linet-services.de>
Wed, 30 Jul 2014 12:40:06 +0000 (14:40 +0200)
committerMoritz Bunkus <m.bunkus@linet-services.de>
Tue, 26 Aug 2014 12:17:56 +0000 (14:17 +0200)
16 files changed:
SL/Controller/RequirementSpecOrder.pm
SL/Controller/RequirementSpecPart.pm [new file with mode: 0644]
SL/DB/Helper/ALL.pm
SL/DB/Helper/Mappings.pm
SL/DB/Manager/RequirementSpecPart.pm [new file with mode: 0644]
SL/DB/MetaSetup/RequirementSpecPart.pm [new file with mode: 0644]
SL/DB/RequirementSpec.pm
SL/DB/RequirementSpecPart.pm [new file with mode: 0644]
js/locale/de.js
js/requirement_spec.js
locale/de/all
sql/Pg-upgrade2/requirement_spec_parts.sql [new file with mode: 0644]
templates/webpages/requirement_spec/show.html
templates/webpages/requirement_spec_part/_edit.html [new file with mode: 0644]
templates/webpages/requirement_spec_part/_part.html [new file with mode: 0644]
templates/webpages/requirement_spec_part/show.html [new file with mode: 0644]

index 8c33d6ffdbf9a8f4d681b6b04e0032f81d1fdda3..7671ffb001ddf5e53ffb67c6926a2702298ed198 100644 (file)
@@ -70,10 +70,7 @@ sub action_create {
   $order->db->with_transaction(sub {
     $order->save;
 
-    $self->requirement_spec->orders(
-      @{ $self->requirement_spec->orders },
-      SL::DB::RequirementSpecOrder->new(order => $order, version => $self->requirement_spec->version)
-    );
+    $self->requirement_spec->add_orders(SL::DB::RequirementSpecOrder->new(order => $order, version => $self->requirement_spec->version));
     $self->requirement_spec->save;
 
     $self->requirement_spec->link_to_record($order);
@@ -247,11 +244,6 @@ sub init_all_parts_time_unit {
 # helpers
 #
 
-sub load_parts_for_sections {
-  my ($self, %params) = @_;
-
-}
-
 sub create_order_item {
   my ($self, %params) = @_;
 
diff --git a/SL/Controller/RequirementSpecPart.pm b/SL/Controller/RequirementSpecPart.pm
new file mode 100644 (file)
index 0000000..b9df7f5
--- /dev/null
@@ -0,0 +1,119 @@
+package SL::Controller::RequirementSpecPart;
+
+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::RequirementSpecPart;
+use SL::Helper::Flash;
+use SL::Locale::String;
+
+use Rose::Object::MakeMethods::Generic
+(
+  'scalar --get_set_init' => [ qw(requirement_spec js) ],
+);
+
+__PACKAGE__->run_before('check_auth');
+
+#
+# actions
+#
+
+sub action_show {
+  my ($self, %params) = @_;
+
+  $self->render('requirement_spec_part/show', { layout => 0 });
+}
+
+sub action_ajax_edit {
+  my ($self, %params) = @_;
+
+  my $html = $self->render('requirement_spec_part/_edit', { output => 0 });
+
+  $self->js
+   ->hide('#additional_parts_list_container')
+   ->after('#additional_parts_list_container', $html)
+   ->on('#edit_additional_parts_form INPUT[type=text]', 'keydown', 'kivi.requirement_spec.additional_parts_input_key_down')
+   ->focus('#additional_parts_add_part_id_name')
+   ->run('kivi.requirement_spec.prepare_edit_additional_parts_form')
+   ->reinit_widgets
+   ->render;
+}
+
+sub action_ajax_add {
+  my ($self)  = @_;
+
+  my $part      = SL::DB::Part->new(id => $::form->{part_id})->load(with_objects => [ qw(unit_obj) ]);
+  my $rs_part   = SL::DB::RequirementSpecPart->new(
+    part        => $part,
+    qty         => 1,
+    unit        => $part->unit_obj,
+    description => $part->description,
+  );
+  my $row       = $self->render('requirement_spec_part/_part', { output => 0 }, part => $rs_part);
+
+  $self->js
+   ->val(  '#additional_parts_add_part_id',      '')
+   ->val(  '#additional_parts_add_part_id_name', '')
+   ->focus('#additional_parts_add_part_id_name')
+   ->append('#edit_additional_parts_list tbody', $row)
+   ->hide('#edit_additional_parts_list_empty')
+   ->show('#edit_additional_parts_list')
+   ->render;
+}
+
+sub action_ajax_save {
+  my ($self) = @_;
+
+  my $db = $self->requirement_spec->db;
+  $db->do_transaction(sub {
+    # Make Emacs happy
+    1;
+    my $parts    = $::form->{additional_parts} || [];
+    my $position = 1;
+    $_->{position} = $position++ for @{ $parts };
+
+    $self->requirement_spec->update_attributes(parts => $parts)->load;
+
+    1;
+  }) or do {
+    return $self->js->error(t8('Saving failed. Error message from the database: #1', $db->error))->render;
+  };
+
+  my $html = $self->render('requirement_spec_part/show', { output => 0 }, initially_hidden => !!$::form->{keep_open});
+
+  $self->js
+    ->replaceWith('#additional_parts_list_container', $html)
+    ->action_if(!$::form->{keep_open}, 'remove', '#additional_parts_form_container')
+    ->render;
+}
+
+#
+# filters
+#
+
+sub check_auth {
+  my ($self, %params) = @_;
+  $::auth->assert('requirement_spec_edit');
+}
+
+#
+# helpers
+#
+
+sub init_js { SL::ClientJS->new(controller => $_[0]) }
+
+sub init_requirement_spec {
+  SL::DB::RequirementSpec->new(id => $::form->{requirement_spec_id})->load(
+    with_objects => [ qw(parts parts.part parts.unit) ],
+  );
+}
+
+1;
index d0cb1cb8bdf006676b0d88d4eb8953faa2e036cc..b60ddf66b058625a22f46cf2f32d24030e2476eb 100644 (file)
@@ -83,6 +83,7 @@ use SL::DB::RequirementSpecComplexity;
 use SL::DB::RequirementSpecDependency;
 use SL::DB::RequirementSpecItem;
 use SL::DB::RequirementSpecOrder;
+use SL::DB::RequirementSpecPart;
 use SL::DB::RequirementSpecPicture;
 use SL::DB::RequirementSpecPredefinedText;
 use SL::DB::RequirementSpecRisk;
index 5acc1c7f4802c551deac3886d33e9b96ccf44621..9695077e792a27ea52c1c3d51032b06d9adf626c 100644 (file)
@@ -163,6 +163,7 @@ my %kivitendo_package_names = (
   requirement_spec_item_dependencies   => 'RequirementSpecDependency',
   requirement_spec_items               => 'RequirementSpecItem',
   requirement_spec_orders              => 'RequirementSpecOrder',
+  requirement_spec_parts               => 'RequirementSpecPart',
   requirement_spec_pictures            => 'RequirementSpecPicture',
   requirement_spec_predefined_texts    => 'RequirementSpecPredefinedText',
   requirement_spec_risks               => 'RequirementSpecRisk',
diff --git a/SL/DB/Manager/RequirementSpecPart.pm b/SL/DB/Manager/RequirementSpecPart.pm
new file mode 100644 (file)
index 0000000..aa82534
--- /dev/null
@@ -0,0 +1,15 @@
+# This file has been auto-generated only because it didn't exist.
+# Feel free to modify it at will; it will not be overwritten automatically.
+
+package SL::DB::Manager::RequirementSpecPart;
+
+use strict;
+
+use SL::DB::Helper::Manager;
+use base qw(SL::DB::Helper::Manager);
+
+sub object_class { 'SL::DB::RequirementSpecPart' }
+
+__PACKAGE__->make_manager_methods;
+
+1;
diff --git a/SL/DB/MetaSetup/RequirementSpecPart.pm b/SL/DB/MetaSetup/RequirementSpecPart.pm
new file mode 100644 (file)
index 0000000..b29ae42
--- /dev/null
@@ -0,0 +1,41 @@
+# This file has been auto-generated. Do not modify it; it will be overwritten
+# by rose_auto_create_model.pl automatically.
+package SL::DB::RequirementSpecPart;
+
+use strict;
+
+use base qw(SL::DB::Object);
+
+__PACKAGE__->meta->table('requirement_spec_parts');
+
+__PACKAGE__->meta->columns(
+  id                  => { type => 'serial', not_null => 1 },
+  description         => { type => 'text', not_null => 1 },
+  part_id             => { type => 'integer', not_null => 1 },
+  position            => { type => 'integer', not_null => 1 },
+  qty                 => { type => 'numeric', not_null => 1, precision => 15, scale => 5 },
+  requirement_spec_id => { type => 'integer', not_null => 1 },
+  unit_id             => { type => 'integer', not_null => 1 },
+);
+
+__PACKAGE__->meta->primary_key_columns([ 'id' ]);
+
+__PACKAGE__->meta->foreign_keys(
+  part => {
+    class       => 'SL::DB::Part',
+    key_columns => { part_id => 'id' },
+  },
+
+  requirement_spec => {
+    class       => 'SL::DB::RequirementSpec',
+    key_columns => { requirement_spec_id => 'id' },
+  },
+
+  unit => {
+    class       => 'SL::DB::Unit',
+    key_columns => { unit_id => 'id' },
+  },
+);
+
+1;
+;
index e5dae7ebd563dbfabcefd2b5c94bfa959afd85e8..235444047f64baf8ff3d14168457c7729c44c1f0 100644 (file)
@@ -43,6 +43,11 @@ __PACKAGE__->meta->add_relationship(
     class          => 'SL::DB::RequirementSpecOrder',
     column_map     => { id => 'requirement_spec_id' },
   },
+  parts            => {
+    type           => 'one to many',
+    class          => 'SL::DB::RequirementSpecPart',
+    column_map     => { id => 'requirement_spec_id' },
+  },
 );
 
 __PACKAGE__->meta->initialize;
@@ -119,6 +124,14 @@ sub versioned_copies_sorted {
   return \@copies;
 }
 
+sub parts_sorted {
+  my ($self, @rest) = @_;
+
+  croak "This sub is not a writer" if @rest;
+
+  return [ sort { $a->position <=> $b->position } @{ $self->parts } ];
+}
+
 sub create_copy {
   my ($self, %params) = @_;
 
@@ -519,6 +532,11 @@ column in ascending order. If the C<output_position> parameter is
 given then only the text blocks belonging to that C<output_position>
 are returned.
 
+=item C<parts_sorted>
+
+Returns an array reference of additional parts sorted by their
+positional column in ascending order.
+
 =item C<validate>
 
 Validate values before saving. Returns list or human-readable error
diff --git a/SL/DB/RequirementSpecPart.pm b/SL/DB/RequirementSpecPart.pm
new file mode 100644 (file)
index 0000000..a511d0b
--- /dev/null
@@ -0,0 +1,11 @@
+package SL::DB::RequirementSpecPart;
+
+use strict;
+
+use SL::DB::MetaSetup::RequirementSpecPart;
+use SL::DB::Manager::RequirementSpecPart;
+use SL::DB::Helper::ActsAsList;
+
+__PACKAGE__->meta->initialize;
+
+1;
index 3b7e86524b584d82e98ac442742f3261c4eff327..e00362dba3eafa54e32944eec17da81dd875f091 100644 (file)
@@ -7,6 +7,7 @@ namespace("kivi").setupLocale({
 "Add section":"Abschnitt hinzufügen",
 "Add sub function block":"Unterfunktionsblock hinzufügen",
 "Add text block":"Textblock erfassen",
+"Additional articles actions":"Aktionen zu zusätzlichen Artikeln",
 "Are you sure?":"Sind Sie sicher?",
 "Basic settings actions":"Aktionen zu Grundeinstellungen",
 "Cancel":"Abbrechen",
@@ -44,6 +45,7 @@ namespace("kivi").setupLocale({
 "Paste template":"Vorlage einfügen",
 "Project link actions":"Projektverknüpfungs-Aktionen",
 "Quotations/Orders actions":"Aktionen für Angebote/Aufträge",
+"Remove article":"Artikel entfernen",
 "Requirement spec actions":"Pflichtenheftaktionen",
 "Requirement spec template actions":"Pflichtenheftvorlagen-Aktionen",
 "Revert to version":"Auf Version zurücksetzen",
index 5078971672bf65dd6fa592413f9727c0c1e6f0d9..b1d5ce85f7e8e7c878d24e6fa7014170c5af2931 100644 (file)
@@ -644,6 +644,121 @@ ns.revert_to_versioned_copy_ajax_call = function(key, opt) {
   return true;
 };
 
+// -------------------------------------------------------------------------
+// -------------------------- time/cost estimate ---------------------------
+// -------------------------------------------------------------------------
+
+ns.standard_time_cost_estimate_ajax_call = function(key, opt) {
+  if (key == 'cancel') {
+    if (confirm(kivi.t8('Do you really want to cancel?'))) {
+      $('#time_cost_estimate').show();
+      $('#time_cost_estimate_form_container').remove();
+    }
+    return true;
+  }
+
+  var add_data = '';
+  if (key == 'save_keep_open') {
+    key      = 'save';
+    add_data = 'keep_open=1&';
+  }
+
+  var data = "action=RequirementSpec/ajax_" + key + "_time_and_cost_estimate&" + add_data;
+
+  if (key == 'save')
+    data += $('#edit_time_cost_estimate_form').serialize()
+         +  '&' + $('#current_content_type').serialize()
+         +  '&' + $('#current_content_id').serialize();
+  else
+    data += 'id=' + encodeURIComponent($('#requirement_spec_id').val());
+
+  $.post("controller.pl", data, kivi.eval_json_result);
+
+  return true;
+};
+
+ns.time_cost_estimate_input_key_down = function(event) {
+  if(event.keyCode == 13) {
+    event.preventDefault();
+    ns.standard_time_cost_estimate_ajax_call('save');
+    return false;
+  }
+};
+
+// -------------------------------------------------------------------------
+// -------------------------- additional parts -----------------------------
+// -------------------------------------------------------------------------
+
+ns.standard_additional_parts_ajax_call = function(key, opt) {
+  var add_data = '';
+  if (key == 'save_keep_open') {
+    key      = 'save';
+    add_data = 'keep_open=1&';
+  }
+
+  var data = "action=RequirementSpecPart/ajax_" + key + "&" + add_data + 'requirement_spec_id=' + encodeURIComponent($('#requirement_spec_id').val()) + '&';
+
+  if (key == 'save')
+    data += $('#edit_additional_parts_form').serialize();
+
+  $.post("controller.pl", data, kivi.eval_json_result);
+
+  return true;
+};
+
+ns.prepare_edit_additional_parts_form = function() {
+  $("#edit_additional_parts_list tbody").sortable({
+    distance: 5,
+    handle:   '.dragdrop',
+    helper:   function(event, ui) {
+      ui.children().each(function() {
+        $(this).width($(this).width());
+      });
+      return ui;
+    }
+
+  });
+};
+
+ns.cancel_edit_additional_parts_form = function() {
+  if (confirm(kivi.t8('Do you really want to cancel?'))) {
+    $('#additional_parts_list_container').show();
+    $('#additional_parts_form_container').remove();
+  }
+  return true;
+};
+
+ns.additional_parts_input_key_down = function(event) {
+  if(event.keyCode == 13) {
+    event.preventDefault();
+    ns.standard_additional_parts_ajax_call('save');
+    return false;
+  }
+};
+
+ns.add_additional_part = function() {
+  var part_id = $('#additional_parts_add_part_id').val();
+  if (!part_id || (part_id == ''))
+    return false;
+
+  var rspec_id = $('#requirement_spec_id').val();
+  var data     = 'action=RequirementSpecPart/ajax_add&requirement_spec_id=' + encodeURIComponent(rspec_id) + '&part_id=' + encodeURIComponent(part_id);
+
+  $.post("controller.pl", data, kivi.eval_json_result);
+
+  return true;
+};
+
+ns.delete_additional_part = function(key, opt) {
+  opt.$trigger.remove();
+  if (!$('#edit_additional_parts_list tbody tr').size()) {
+   $('#edit_additional_parts_list_empty').show();
+   $('#edit_additional_parts_list').hide();
+  }
+
+  return true;
+};
+
 // -------------------------------------------------------------------------
 // ------------------------------- tab widget ------------------------------
 // -------------------------------------------------------------------------
@@ -651,8 +766,9 @@ var content_div_ids_for_tab_headers = {
     'tab-header-function-block':     'function-blocks-tab'
   , 'tab-header-basic-settings':     'ui-tabs-1'
   , 'tab-header-time-cost-estimate': 'ui-tabs-2'
-  , 'tab-header-versions':           'ui-tabs-3'
-  , 'tab-header-quotations-orders':  'ui-tabs-4'
+  , 'tab-header-additional-parts':   'ui-tabs-3'
+  , 'tab-header-versions':           'ui-tabs-4'
+  , 'tab-header-quotations-orders':  'ui-tabs-5'
 };
 
 ns.tabs_before_activate = function(event, ui) {
@@ -804,6 +920,35 @@ ns.create_context_menus = function(is_template) {
     }, general_actions)
   });
 
+  $.contextMenu({
+    selector: '.additional-parts-context-menu',
+    items:    $.extend({
+        heading: { name: kivi.t8('Additional articles actions'), className: 'context-menu-heading' }
+      , edit:    { name: kivi.t8('Edit'), icon: "edit", callback: kivi.requirement_spec.standard_additional_parts_ajax_call }
+    }, general_actions)
+  });
+
+  var additional_parts_actions = {
+      save:           { name: kivi.t8('Save'),               icon: "save",  callback: kivi.requirement_spec.standard_additional_parts_ajax_call }
+    , save_keep_open: { name: kivi.t8('Save and keep open'), icon: "save",  callback: kivi.requirement_spec.standard_additional_parts_ajax_call }
+    , cancel:         { name: kivi.t8('Cancel'),             icon: "close",  callback: kivi.requirement_spec.cancel_edit_additional_parts_form }
+  };
+
+  $.contextMenu({
+    selector: '.edit-additional-parts-context-menu',
+    items:    $.extend({
+        heading:        { name: kivi.t8('Additional articles actions'), className: 'context-menu-heading' }
+    }, additional_parts_actions, general_actions)
+  });
+
+  $.contextMenu({
+    selector: '.edit-additional-parts-row-context-menu',
+    items:    $.extend({
+        heading:        { name: kivi.t8('Additional articles actions'), className: 'context-menu-heading' }
+      , delete:         { name: kivi.t8('Remove article'),     icon: "delete", callback: kivi.requirement_spec.delete_additional_part }
+    }, additional_parts_actions, general_actions)
+  });
+
   $.contextMenu({
     selector: '.quotations-and-orders-context-menu,.quotations-and-orders-order-context-menu',
     items:    $.extend({
index 2c31b40097a038b33b18de613e0b08a70591165e..d6664b5fa03995aa25eb1a8800368d5f57f87acb 100755 (executable)
@@ -176,6 +176,7 @@ $self->{texts} = {
   'Add new currency'            => 'Neue Währung hinzufügen',
   'Add new custom variable'     => 'Neue benutzerdefinierte Variable erfassen',
   'Add note'                    => 'Notiz erfassen',
+  'Add part'                    => 'Artikel hinzufügen',
   'Add picture'                 => 'Bild hinzufügen',
   'Add picture to text block'   => 'Bild dem Textblock hinzufügen',
   'Add section'                 => 'Abschnitt hinzufügen',
@@ -185,6 +186,8 @@ $self->{texts} = {
   'Add unit'                    => 'Einheit hinzuf&uuml;gen',
   'Added sections and function blocks: #1' => 'Hinzugefügte Abschnitte und Funktionsblöcke: #1',
   'Added text blocks: #1'       => 'Hinzugefügte Textblöcke: #1',
+  'Additional articles'         => 'Zusätzliche Artikel',
+  'Additional articles actions' => 'Aktionen zu zusätzlichen Artikeln',
   'Address'                     => 'Adresse',
   'Admin'                       => 'Administration',
   'Administration'              => 'Administration',
@@ -933,6 +936,7 @@ $self->{texts} = {
   'Edit Vendor Invoice'         => 'Einkaufsrechnung bearbeiten',
   'Edit Warehouse'              => 'Lager bearbeiten',
   'Edit acceptance status'      => 'Abnahmestatus bearbeiten',
+  'Edit additional articles'    => 'Zusätzliche Artikel bearbeiten',
   'Edit article/section assignments' => 'Zuweisung Artikel/Abschnitte bearbeiten',
   'Edit assignment of articles to sections' => 'Zuweisung Artikel zu Abschnitten bearbeiten',
   'Edit background job'         => 'Hintergrund-Job bearbeiten',
@@ -1520,6 +1524,7 @@ $self->{texts} = {
   'No Vendor was found matching the search parameters.' => 'Zu dem Suchbegriff wurde kein Händler gefunden',
   'No acceptance statuses has been created yet.' => 'Es wurde noch kein Abnahmestatus angelegt.',
   'No action defined.'          => 'Keine Aktion definiert.',
+  'No articles have been added yet.' => 'Es wurden noch keine Artikel hinzugefügt.',
   'No background job has been created yet.' => 'Es wurden noch keine Hintergrund-Jobs angelegt.',
   'No bank information has been entered in this customer\'s master data entry. You cannot create bank collections unless you enter bank information.' => 'Für diesen Kunden wurden in seinen Stammdaten keine Kontodaten hinterlegt. Solange dies nicht geschehen ist, können Sie keine Überweisungen für den Lieferanten anlegen.',
   'No bank information has been entered in this vendor\'s master data entry. You cannot create bank transfers unless you enter bank information.' => 'Für diesen Lieferanten wurden in seinen Stammdaten keine Kontodaten hinterlegt. Solange dies nicht geschehen ist, können Sie keine Überweisungen für den Lieferanten anlegen.',
@@ -1938,6 +1943,7 @@ $self->{texts} = {
   'Removal qty'                 => 'Entnahmemenge',
   'Remove'                      => 'Entfernen',
   'Remove Draft'                => 'Entwurf l&ouml;schen',
+  'Remove article'              => 'Artikel entfernen',
   'Remove draft when posting'   => 'Entwurf beim Buchen l&ouml;schen',
   'Removed sections and function blocks: #1' => 'Entfernte Abschnitte und Funktionsblöcke: #1',
   'Removed spoolfiles!'         => 'Druckdateien entfernt!',
diff --git a/sql/Pg-upgrade2/requirement_spec_parts.sql b/sql/Pg-upgrade2/requirement_spec_parts.sql
new file mode 100644 (file)
index 0000000..723eab6
--- /dev/null
@@ -0,0 +1,17 @@
+-- @tag: requirement_spec_parts
+-- @description: Artikelzuweisung zu Pflichtenheften
+-- @depends: release_3_1_0
+CREATE TABLE requirement_spec_parts (
+  id                  SERIAL         NOT NULL,
+  requirement_spec_id INTEGER        NOT NULL,
+  part_id             INTEGER        NOT NULL,
+  unit_id             INTEGER        NOT NULL,
+  qty                 NUMERIC(15, 5) NOT NULL,
+  description         TEXT           NOT NULL,
+  position            INTEGER        NOT NULL,
+
+  PRIMARY KEY (id),
+  FOREIGN KEY (requirement_spec_id) REFERENCES requirement_specs (id),
+  FOREIGN KEY (part_id)             REFERENCES parts             (id),
+  FOREIGN KEY (unit_id)             REFERENCES units             (id)
+);
index c41e87d84ca9895f5852cea38afb9ad5a78b1878..756fbc85c731b0475b300addf282c663aa3a91a1 100644 (file)
@@ -12,6 +12,7 @@
   <li id="tab-header-function-block"><a href="#function-blocks-tab">[%- LxERP.t8("Content") %]</a></li>
   <li id="tab-header-basic-settings"><a href="controller.pl?action=RequirementSpec/ajax_show_basic_settings&id=[% HTML.url(SELF.requirement_spec.id) %]">[%- LxERP.t8("Basic settings") %]</a></li>
   <li id="tab-header-time-cost-estimate"><a href="controller.pl?action=RequirementSpec/ajax_show_time_and_cost_estimate&id=[% HTML.url(SELF.requirement_spec.id) %]">[%- LxERP.t8("Time and cost estimate") %]</a></li>
+  <li id="tab-header-additional-parts"><a href="controller.pl?action=RequirementSpecPart/show&requirement_spec_id=[% HTML.url(SELF.requirement_spec.id) %]">[%- LxERP.t8("Additional articles") %]</a></li>
   [%- UNLESS SELF.requirement_spec.is_template %]
    <li id="tab-header-versions"><a href="controller.pl?action=RequirementSpecVersion/list&requirement_spec_id=[% HTML.url(SELF.requirement_spec.id) %]">[%- LxERP.t8("Versions") %]</a></li>
    <li id="tab-header-quotations-orders"><a href="[% SELF.url_for(controller='RequirementSpecOrder', action='list', requirement_spec_id=SELF.requirement_spec.id) %]">[%- LxERP.t8("Quotations and orders") %]</a></li>
diff --git a/templates/webpages/requirement_spec_part/_edit.html b/templates/webpages/requirement_spec_part/_edit.html
new file mode 100644 (file)
index 0000000..c1b2795
--- /dev/null
@@ -0,0 +1,38 @@
+[%- USE LxERP -%][%- USE L -%][%- USE P -%]
+[% SET parts = SELF.requirement_spec.parts_sorted %]
+
+<div id="additional_parts_form_container" class="edit-additional-parts-context-menu">
+
+ <h2>[% LxERP.t8("Edit additional articles") %]</h2>
+
+ <div>
+  [% LxERP.t8("Add part") %]:
+  [% P.part_picker('additional_parts_add_part_id', '', style="width: 300px") %]
+  [% L.button_tag('kivi.requirement_spec.add_additional_part()', LxERP.t8('Add part')) %]
+ </div>
+
+ <form method="post" id="edit_additional_parts_form">
+  <div id="edit_additional_parts_list_empty"[% IF parts.size %] style="display: none;"[% END %]>
+   [% LxERP.t8("No articles have been added yet.") %]
+  </div>
+
+  <table id="edit_additional_parts_list"[% IF !parts.size %] style="display: none;"[% END %]>
+   <thead>
+    <tr class="listheading">
+     <th></th>
+     <th>[%- LxERP.t8("Part Number") %]</th>
+     <th>[%- LxERP.t8("Description") %]</th>
+     <th>[%- LxERP.t8("Qty") %]</th>
+    </tr>
+   </thead>
+
+   <tbody>
+    [%- FOREACH part = parts %]
+     [%- INCLUDE 'requirement_spec_part/_part.html' part=part %]
+    [%- END %]
+   </tbody>
+  </table>
+
+   [% L.button_tag("kivi.requirement_spec.standard_additional_parts_ajax_call('save')", LxERP.t8("Save")) %]
+  </form>
+</div>
diff --git a/templates/webpages/requirement_spec_part/_part.html b/templates/webpages/requirement_spec_part/_part.html
new file mode 100644 (file)
index 0000000..6136de0
--- /dev/null
@@ -0,0 +1,14 @@
+[%- USE HTML -%][%- USE L -%][%- USE LxERP -%]
+<tr class="listrow edit-additional-parts-row-context-menu">
+ <td align="center">
+  [% L.hidden_tag("additional_parts[+].part_id", part.part.id) %]
+  [% L.hidden_tag("additional_parts[].id", part.id) %]
+  [% L.img_tag(src="image/updown.png", alt=LxERP.t8("reorder item"), class="dragdrop") %]
+ </td>
+ <td>[% HTML.escape(part.part.partnumber) %]</td>
+ <td>[% L.input_tag("additional_parts[].description", part.description, size="30") %]</td>
+ <td>
+  [% L.input_tag("additional_parts[].qty_as_number", part.qty_as_number, size="10") %]
+  [% L.select_tag("additional_parts[].unit_id", part.unit.convertible_units, title_key="name", default=part.unit.id) %]
+ </td>
+</tr>
diff --git a/templates/webpages/requirement_spec_part/show.html b/templates/webpages/requirement_spec_part/show.html
new file mode 100644 (file)
index 0000000..128c7e0
--- /dev/null
@@ -0,0 +1,31 @@
+[%- USE LxERP -%][%- USE L -%][%- USE P -%][%- USE HTML -%]
+[% SET parts = SELF.requirement_spec.parts_sorted %]
+
+<div id="additional_parts_list_container" class="additional-parts-context-menu"[% IF initially_hidden %] style="display: none;"[% END %]>
+
+ <h2>[% LxERP.t8("Additional articles") %]</h2>
+
+ <div id="additional_parts_list_empty"[% IF parts.size %] style="display: none;"[% END %]>
+  [% LxERP.t8("No articles have been added yet.") %]
+ </div>
+
+ <table id="additional_parts_list"[% IF !parts.size %] style="display: none;"[% END %]>
+  <thead>
+   <tr class="listheading">
+    <th>[%- LxERP.t8("Part Number") %]</th>
+    <th>[%- LxERP.t8("Description") %]</th>
+    <th>[%- LxERP.t8("Qty") %]</th>
+   </tr>
+  </thead>
+
+  <tbody>
+   [% FOREACH part = parts %]
+    <tr class="listrow">
+     <td>[% HTML.escape(part.part.partnumber) %]</td>
+     <td>[% HTML.escape(part.description) %]</td>
+     <td valign="right">[% HTML.escape(part.qty_as_number) %] [% HTML.escape(part.unit.name) %]</td>
+    </tr>
+   [% END %]
+  </tbody>
+ </table>
+</div>