commit 8a721ec3ad3d11a8d0a59234a6945b46ec0a9c99
Author: Bernd Bleßmann <bernd@kivitendo-premium.de>
Date:   Wed Jul 25 11:31:43 2012 +0200

    Erweiterung für Artikelnummern nach Kunden-/Lieferanten-Typ (business).
    
    Suche im Bericht und Import sind (noch) nicht implementiert.

diff --git a/SL/Controller/Part.pm b/SL/Controller/Part.pm
index e9ba45b94..abe886b6d 100644
--- a/SL/Controller/Part.pm
+++ b/SL/Controller/Part.pm
@@ -15,6 +15,8 @@ use Text::CSV_XS;
 
 use SL::CVar;
 use SL::Controller::Helper::GetModels;
+use SL::DB::Business;
+use SL::DB::BusinessModel;
 use SL::DB::Helper::ValidateAssembly qw(validate_assembly);
 use SL::DB::History;
 use SL::DB::Part;
@@ -31,14 +33,15 @@ use SL::Presenter::Tag qw(select_tag);
 
 use Rose::Object::MakeMethods::Generic (
   'scalar --get_set_init' => [ qw(parts models part p warehouses multi_items_models
-                                  makemodels shops_not_assigned
+                                  makemodels businessmodels shops_not_assigned
                                   customerprices
                                   orphaned
                                   assortment assortment_items assembly assembly_items
                                   all_pricegroups all_translations all_partsgroups all_units
                                   all_buchungsgruppen all_payment_terms all_warehouses
                                   parts_classification_filter
-                                  all_languages all_units all_price_factors) ],
+                                  all_languages all_units all_price_factors
+                                  all_businesses) ],
   'scalar'                => [ qw(warehouse bin stock_amounts journal) ],
 );
 
@@ -525,6 +528,36 @@ sub action_add_makemodel_row {
     ->render;
 }
 
+sub action_add_businessmodel_row {
+  my ($self) = @_;
+
+  my $business_id = $::form->{add_businessmodel};
+
+  my $business = SL::DB::Manager::Business->find_by(id => $business_id) or
+    return $self->js->error(t8("No business selected or found!"))->render;
+
+  if ( grep { $business_id == $_->business_id } @{ $self->businessmodels } ) {
+    $self->js->flash('info', t8("This business has already been added."));
+  };
+
+  my $bm = SL::DB::BusinessModel->new(#parts_id             => $::form->{part}->{id},
+                                      business             => $business,
+                                      model                => '',
+                                      part_description     => '',
+                                      part_longdescription => '',
+  ) or die "Can't create BusinessModel object";
+
+  my $row_as_html = $self->p->render('part/_businessmodel_row',
+                                     businessmodel => $bm);
+
+  # after selection focus on the model field in the row that was just added
+  $self->js
+    ->append('#businessmodel_rows', $row_as_html)  # append in tbody
+    ->val('#add_businessmodel', '')
+    ->run('kivi.Part.focus_last_businessmodel_input')
+    ->render;
+}
+
 sub action_add_customerprice_row {
   my ($self) = @_;
 
@@ -906,6 +939,7 @@ sub parse_form {
 
   $self->parse_form_customerprices;
   $self->parse_form_makemodels;
+  $self->parse_form_businessmodels;
 }
 
 sub parse_form_prices {
@@ -977,6 +1011,33 @@ sub parse_form_makemodels {
   };
 }
 
+sub parse_form_businessmodels {
+  my ($self) = @_;
+
+  my $make_key = sub { return $_[0]->parts_id . '+' . $_[0]->business_id; };
+
+  my $businessmodels_map;
+  if ( $self->part->businessmodels ) { # check for new parts or parts without businessmodels
+    $businessmodels_map = { map { $make_key->($_) => Rose::DB::Object::Helpers::clone($_) } @{$self->part->businessmodels} };
+  };
+
+  $self->part->businessmodels([]);
+
+  my $businessmodels = delete($::form->{businessmodels}) || [];
+  foreach my $businessmodel ( @{$businessmodels} ) {
+    next unless $businessmodel->{business_id};
+
+    my $bm = SL::DB::BusinessModel->new( #parts_id            => $self->part->id,            # will be assigned by row add_businessmodels
+                                         business_id          => $businessmodel->{business_id},
+                                         model                => $businessmodel->{model} || '',
+                                         part_description     => $businessmodel->{part_description} || '',
+                                         part_longdescription => $businessmodel->{part_longdescription} || '',
+    );
+
+    $self->part->add_businessmodels($bm);
+  };
+}
+
 sub parse_form_customerprices {
   my ($self) = @_;
 
@@ -1044,7 +1105,7 @@ sub init_part {
   # used by edit, save, delete and add
 
   if ( $::form->{part}{id} ) {
-    return SL::DB::Part->new(id => $::form->{part}{id})->load(with => [ qw(makemodels customerprices prices translations partsgroup shop_parts shop_parts.shop) ]);
+    return SL::DB::Part->new(id => $::form->{part}{id})->load(with => [ qw(makemodels businessmodels customerprices prices translations partsgroup shop_parts shop_parts.shop) ]);
   } elsif ( $::form->{id} ) {
     return SL::DB::Part->new(id => $::form->{id})->load; # used by inventory tab
   } else {
@@ -1127,6 +1188,28 @@ sub init_makemodels {
   return \@makemodel_array;
 }
 
+sub init_businessmodels {
+  my ($self) = @_;
+
+  my @businessmodel_array = ();
+  my $businessmodels = delete($::form->{businessmodels}) || [];
+
+  foreach my $businessmodel ( @{$businessmodels} ) {
+    next unless $businessmodel->{business_id};
+
+    my $bm = SL::DB::BusinessModel->new(#parts_id            => $self->part->id,             # will be assigned by row add_businessmodels
+                                        business_id          => $businessmodel->{business_id},
+                                        model                => $businessmodel->{model} || '',
+                                        part_description     => $businessmodel->{part_description} || '',
+                                        part_longdescription => $businessmodel->{part_longdescription} || '',
+                                  ) or die "Can't create bm";
+
+    push(@businessmodel_array, $bm);
+  };
+
+  return \@businessmodel_array;
+}
+
 sub init_customerprices {
   my ($self) = @_;
 
@@ -1235,6 +1318,10 @@ sub init_all_pricegroups {
   SL::DB::Manager::Pricegroup->get_all_sorted(query => [ obsolete => 0 ]);
 }
 
+sub init_all_businesses {
+  SL::DB::Manager::Business->get_all_sorted;
+}
+
 # model used to filter/display the parts in the multi-items dialog
 sub init_multi_items_models {
   SL::Controller::Helper::GetModels->new(
diff --git a/SL/DB/BusinessModel.pm b/SL/DB/BusinessModel.pm
new file mode 100644
index 000000000..765aa9f04
--- /dev/null
+++ b/SL/DB/BusinessModel.pm
@@ -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::BusinessModel;
+
+use strict;
+
+use SL::DB::MetaSetup::BusinessModel;
+
+__PACKAGE__->meta->initialize;
+
+# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
+__PACKAGE__->meta->make_manager_class;
+
+1;
diff --git a/SL/DB/Helper/ALL.pm b/SL/DB/Helper/ALL.pm
index 028b4240f..fc499e3d0 100644
--- a/SL/DB/Helper/ALL.pm
+++ b/SL/DB/Helper/ALL.pm
@@ -27,6 +27,7 @@ use SL::DB::BankTransactionAccTrans;
 use SL::DB::Bin;
 use SL::DB::Buchungsgruppe;
 use SL::DB::Business;
+use SL::DB::BusinessModel;
 use SL::DB::Chart;
 use SL::DB::Contact;
 use SL::DB::ContactDepartment;
diff --git a/SL/DB/Helper/Mappings.pm b/SL/DB/Helper/Mappings.pm
index 0ea54824e..9ab29acbc 100644
--- a/SL/DB/Helper/Mappings.pm
+++ b/SL/DB/Helper/Mappings.pm
@@ -110,6 +110,7 @@ my %kivitendo_package_names = (
   buchungsgruppen                => 'buchungsgruppe',
   bin                            => 'bin',
   business                       => 'business',
+  business_models                => 'business_model',
   chart                          => 'chart',
   contact_departments            => 'contact_department',
   contact_titles                 => 'contact_title',
diff --git a/SL/DB/MetaSetup/BusinessModel.pm b/SL/DB/MetaSetup/BusinessModel.pm
new file mode 100644
index 000000000..f6fe0c9ef
--- /dev/null
+++ b/SL/DB/MetaSetup/BusinessModel.pm
@@ -0,0 +1,38 @@
+# This file has been auto-generated. Do not modify it; it will be overwritten
+# by rose_auto_create_model.pl automatically.
+package SL::DB::BusinessModel;
+
+use strict;
+
+use parent qw(SL::DB::Object);
+
+__PACKAGE__->meta->table('business_models');
+
+__PACKAGE__->meta->columns(
+  business_id          => { type => 'integer', not_null => 1 },
+  itime                => { type => 'timestamp', default => 'now()' },
+  model                => { type => 'text' },
+  mtime                => { type => 'timestamp' },
+  part_description     => { type => 'text' },
+  part_longdescription => { type => 'text' },
+  parts_id             => { type => 'integer', not_null => 1 },
+);
+
+__PACKAGE__->meta->primary_key_columns([ 'parts_id', 'business_id' ]);
+
+__PACKAGE__->meta->allow_inline_column_values(1);
+
+__PACKAGE__->meta->foreign_keys(
+  business => {
+    class       => 'SL::DB::Business',
+    key_columns => { business_id => 'id' },
+  },
+
+  parts => {
+    class       => 'SL::DB::Part',
+    key_columns => { parts_id => 'id' },
+  },
+);
+
+1;
+;
diff --git a/SL/DB/Part.pm b/SL/DB/Part.pm
index 6f23a644b..4133bf8d7 100644
--- a/SL/DB/Part.pm
+++ b/SL/DB/Part.pm
@@ -47,6 +47,11 @@ __PACKAGE__->meta->add_relationships(
     manager_args => { sort_by => 'sortorder' },
     column_map   => { id => 'parts_id' },
   },
+  businessmodels     => {
+    type         => 'one to many',
+    class        => 'SL::DB::BusinessModel',
+    column_map   => { id => 'parts_id' },
+  },
   customerprices => {
     type         => 'one to many',
     class        => 'SL::DB::PartCustomerPrice',
diff --git a/SL/IC.pm b/SL/IC.pm
index 4443d7881..09bb644c7 100644
--- a/SL/IC.pm
+++ b/SL/IC.pm
@@ -909,6 +909,27 @@ sub prepare_parts_for_printing {
 
   $sth->finish();
 
+
+  $query            = qq|SELECT bm.parts_id,
+                         bm.model                AS business_model,
+                         bm.part_description     AS bm_part_description,
+                         bm.part_longdescription AS bm_part_longdescription,
+                         b.description           AS business_make
+                         FROM business_models bm
+                         LEFT JOIN business b ON (bm.business_id = b.id)
+                         WHERE bm.parts_id IN ($placeholders)|;
+
+  my %businessmodel = ();
+
+  $sth              = prepare_execute_query($form, $dbh, $query, @part_ids);
+
+  while (my $ref = $sth->fetchrow_hashref()) {
+    $businessmodel{$ref->{parts_id}} ||= [];
+    push @{ $businessmodel{$ref->{parts_id}} }, $ref;
+  }
+
+  $sth->finish();
+
   $query           = qq|SELECT
                         cp.parts_id,
                         cp.customer_partnumber  AS customer_model,
@@ -939,7 +960,7 @@ sub prepare_parts_for_printing {
   my %data    = selectall_as_map($form, $dbh, $query, 'id', \@columns, @part_ids);
 
   my %template_arrays;
-  map { $template_arrays{$_} = [] } (qw(make model mm_part_description mm_part_longdescription customer_make customer_model cm_part_description cm_part_longdescription), @columns);
+  map { $template_arrays{$_} = [] } (qw(make model mm_part_description mm_part_longdescription business_make business_model bm_part_description bm_part_longdescription customer_make customer_model cm_part_description cm_part_longdescription), @columns);
 
   foreach my $i (1 .. $rowcount) {
     my $id = $form->{"${prefix}${i}"};
@@ -961,6 +982,17 @@ sub prepare_parts_for_printing {
       }
     }
 
+    push @{ $template_arrays{business_make} },           [];
+    push @{ $template_arrays{business_model} },          [];
+    push @{ $template_arrays{bm_part_description} },     [];
+    push @{ $template_arrays{bm_part_longdescription} }, [];
+
+    if ($businessmodel{$id}) {
+      foreach my $ref (@{ $businessmodel{$id} }) {
+        map { push @{ $template_arrays{$_}->[-1] }, $ref->{$_} } qw(business_make business_model bm_part_description bm_part_longdescription);
+      }
+    }
+
     push @{ $template_arrays{customer_make} },  [];
     push @{ $template_arrays{customer_model} }, [];
     push @{ $template_arrays{cm_part_description} },     [];
diff --git a/js/kivi.Part.js b/js/kivi.Part.js
index 6207569f6..f90457ec0 100644
--- a/js/kivi.Part.js
+++ b/js/kivi.Part.js
@@ -264,6 +264,24 @@ namespace('kivi.Part', function(ns) {
     $("#makemodel_rows tr:last").find('input[type=text]').filter(':visible:first').focus();
   };
 
+  // businessmodel
+  ns.delete_businessmodel_row = function(clicked) {
+    var row = $(clicked).closest('tr');
+    $(row).remove();
+  };
+
+  ns.add_businessmodel_row = function() {
+    if ($('#add_businessmodel').val() === '') return;
+
+    var data = $('#businessmodel_table :input').serializeArray();
+    data.push({ name: 'action', value: 'Part/add_businessmodel_row' });
+
+    $.post("controller.pl", data, kivi.eval_json_result);
+  };
+
+  ns.focus_last_businessmodel_input = function () {
+    $("#businessmodel_rows tr:last").find('input[type=text]').filter(':visible:first').focus();
+  };
 
   // customerprice
   ns.customerprice_renumber_positions = function() {
diff --git a/locale/de/all b/locale/de/all
index 68c0ec6e9..21773560e 100644
--- a/locale/de/all
+++ b/locale/de/all
@@ -569,6 +569,8 @@ $self->{texts} = {
   'Business'                    => 'Kunden-/Lieferantentyp',
   'Business Discount'           => 'Kunden-/Lieferantentyp-Rabatt',
   'Business Number'             => 'Firmennummer',
+  'Business Type'               => 'Kunden-/Lieferantentyp',
+  'Business Type Model'         => 'Kunden-/Lieferantentyp-Art.-Nr.',
   'Business Volume'             => 'Geschäftsvolumen',
   'Business evaluation'         => 'Betriebswirtschaftliche Auswertung',
   'Business type (database ID)' => 'Kunden-/Lieferantentyp (Datenbank-ID)',
diff --git a/locale/en/all b/locale/en/all
index a357679e1..ed077b943 100644
--- a/locale/en/all
+++ b/locale/en/all
@@ -569,6 +569,8 @@ $self->{texts} = {
   'Business'                    => '',
   'Business Discount'           => '',
   'Business Number'             => '',
+  'Business Type'               => '',
+  'Business Type Model'         => '',
   'Business Volume'             => '',
   'Business evaluation'         => '',
   'Business type (database ID)' => '',
diff --git a/sql/Pg-upgrade2/add_business_models.sql b/sql/Pg-upgrade2/add_business_models.sql
new file mode 100644
index 000000000..511e1bb6f
--- /dev/null
+++ b/sql/Pg-upgrade2/add_business_models.sql
@@ -0,0 +1,22 @@
+-- @tag: add_business_models
+-- @description: Tabelle für Kunden-/Lieferantentyp-Artikelnummern und Beschreibung
+-- @depends: release_3_8_0
+
+CREATE TABLE business_models (
+  parts_id               integer NOT NULL,
+  business_id            integer NOT NULL,
+  model                  text,
+  part_description       text,
+  part_longdescription   text,
+
+  itime                  timestamp              DEFAULT now(),
+  mtime                  timestamp,
+
+  FOREIGN KEY (parts_id)    REFERENCES parts(id),
+  FOREIGN KEY (business_id) REFERENCES business(id),
+
+  PRIMARY KEY(parts_id, business_id)
+);
+
+CREATE TRIGGER mtime_business_models BEFORE UPDATE ON business_models
+    FOR EACH ROW EXECUTE PROCEDURE set_mtime();
diff --git a/templates/webpages/part/_basic_data.html b/templates/webpages/part/_basic_data.html
index a9fa531ca..a8129ae4a 100644
--- a/templates/webpages/part/_basic_data.html
+++ b/templates/webpages/part/_basic_data.html
@@ -236,6 +236,7 @@
 [%- UNLESS SELF.part.is_assembly %]
  [% PROCESS 'part/_makemodel.html' %]
 [% END %]
+ [% PROCESS 'part/_businessmodels.html' %]
 
   <tr>
     <td><hr size="3" noshade></td>
diff --git a/templates/webpages/part/_businessmodel_row.html b/templates/webpages/part/_businessmodel_row.html
new file mode 100644
index 000000000..665145b1c
--- /dev/null
+++ b/templates/webpages/part/_businessmodel_row.html
@@ -0,0 +1,15 @@
+[%- USE T8 %]
+[%- USE L %]
+[%- USE HTML %]
+[%- USE LxERP %]
+        <tr class="listrow businessmodel_row">
+         <td align="center">
+           [%- L.hidden_tag("businessmodels[+].business_id", businessmodel.business_id) -%]
+           [%- L.button_tag("kivi.Part.delete_businessmodel_row(this)",
+                            LxERP.t8("X")) %] [% # , confirm=LxERP.t8("Are you sure?")) %]
+         </td>
+         <td>[% businessmodel.business.description | html %]</td>
+         <td>[% L.input_tag('businessmodels[].model', businessmodel.model, size=30 ) %]</td>
+         <td>[% L.input_tag('businessmodels[].part_description',         businessmodel.part_description,     size=30 ) %]</td>
+         <td>[% L.areainput_tag('businessmodels[].part_longdescription', businessmodel.part_longdescription, size=30 ) %]
+        </tr>
diff --git a/templates/webpages/part/_businessmodels.html b/templates/webpages/part/_businessmodels.html
new file mode 100644
index 000000000..86af6c16c
--- /dev/null
+++ b/templates/webpages/part/_businessmodels.html
@@ -0,0 +1,36 @@
+[%- USE T8 %]
+[%- USE L %]
+[%- USE P %]
+[%- USE HTML %]
+[%- USE LxERP %]
+  <tr>
+  </tr>
+  <tr>
+    <td>
+      <table id="businessmodel_table">
+        <thead>
+         <tr>
+          <th class="listheading" style='text-align:center' nowrap width="1"><img src="image/close.png" alt="[%- LxERP.t8('delete item') %]"></th>
+          <th class="listheading">[% 'Business'            | $T8 %]</th>
+          <th class="listheading">[% 'Business Type Model' | $T8 %]</th>
+          <th class="listheading">[% 'Part Description'    | $T8 %]</th>
+          <th class="listheading">[% 'Long Description'    | $T8 %]</th>
+         </tr>
+        </thead>
+        <tbody id="businessmodel_rows">
+        [%- FOREACH businessmodel = SELF.part.businessmodels %]
+          [% PROCESS 'part/_businessmodel_row.html' businessmodel=businessmodel %]
+        [%- END %]
+       </tbody>
+       <tbody>
+        <tr>
+         <td></td>
+         <td align="right">[% 'Business' | $T8 %]</td>
+         <td>[% P.select_tag('add_businessmodel', SELF.all_businesses, title_key="description", with_empty=1, style='width: 300px', onchange='kivi.Part.add_businessmodel_row()') %]</td>
+         <td align="right">[% L.button_tag('kivi.Part.add_businessmodel_row()', LxERP.t8('Add')) %]</td>
+         <td></td>
+        </tr>
+       </tbody>
+      </table>
+    </td>
+  </tr>
