Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 8f7c2c63

Von Tamino Steinert vor mehr als 1 Jahr hinzugefügt

  • ID 8f7c2c63c73cfa2d78620a61f79c588c23e9954c
  • Vorgänger 45d80d98
  • Nachfolger 0c7b83b5

TMP DispositionManager aktueller stand

Unterschiede anzeigen:

SL/Controller/DispositionManager.pm
8 8
use SL::Controller::Helper::ReportGenerator;
9 9
use SL::DB::Part;
10 10
use SL::DB::PurchaseBasketItem;
11
use SL::DB::Order;
12
use SL::DB::Vendor;
11 13
use SL::PriceSource;
12 14
use SL::Locale::String qw(t8);
13 15
use SL::Helper::Flash qw(flash);
......
15 17

  
16 18
use Data::Dumper;
17 19

  
18
use Rose::Object::MakeMethods::Generic
19
(
20
use Rose::Object::MakeMethods::Generic (
20 21
 'scalar --get_set_init' => [ qw(models) ],
21 22
);
22 23

  
23 24
sub action_list_parts {
24
  my ( $self ) = @_;
25
  my ($self) = @_;
25 26
  $self->{action} = 'list_parts';
26
  $self->prepare_report('Reorder Level List', $::form->{noshow} ? 1 : 0 );
27
  $self->prepare_report(t8('Reorder Level List'), $::form->{noshow} ? 1 : 0 );
27 28

  
28 29
  my $objects = $::form->{noshow} ? [] : $self->models->get;
29 30

  
30 31
  $self->_setup_list_action_bar;
31
  $self->report_generator_list_objects(report => $self->{report}, objects => $objects);
32
  $self->report_generator_list_objects(
33
    report => $self->{report}, objects => $objects);
32 34
}
33 35

  
34 36
sub prepare_report {
35 37
  my ($self, $title, $noshow ) = @_;
36
  $main::lxdebug->enter_sub();
37 38

  
38
  my $locale   = $main::locale;
39 39
  my $report = SL::ReportGenerator->new(\%::myconfig, $::form);
40 40
  $self->{report} = $report;
41 41

  
42
  my @columns  = qw(partnumber description drawing available onhand rop ordered); # consume);
43
  my @visible  = qw(partnumber description drawing available onhand rop ordered); # consume);
44
  my @sortable = qw(partnumber description); # consume);
42
  my @columns  = qw(
43
    partnumber description available onhand rop ordered
44
    );
45
  my @visible  = qw(
46
    partnumber description available onhand rop ordered
47
    );
48
  my @sortable = qw(partnumber description);
45 49

  
46 50
  my %column_defs = (
47
    'partnumber'                  => { sub                                   => sub { $_[0]->partnumber }, text                                                   => t8('Part Number'),
48
                         obj_link => sub { $_[0]->presenter->link_to },   },
49
    'description'                 => { sub                                   => sub { $_[0]->description }, text                                                  => t8('Part Description'),
50
                         obj_link => sub { $_[0]->presenter->link_to },   },
51
    'drawing'                     => { sub                                   => sub { $_[0]->drawing }, text                                                      => t8('Drawing'), },
52
    'available'                   => { sub                                   => sub { $::form->format_amount(\%::myconfig,$_[0]->onhandqty,2); }, text            => t8('Available Stock'), },
53
    'onhand'                      => { sub                                   => sub { $::form->format_amount(\%::myconfig,$_[0]->stockqty,2); }, text             => t8('All On Hand'), },
54
    'rop'                         => { sub                                   => sub { $::form->format_amount(\%::myconfig,$_[0]->rop,2); }, text                  => t8('Rop'), },
55
    'ordered'                     => { sub                                   => sub { $::form->format_amount(\%::myconfig,$_[0]->get_open_ordered_qty,2); }, text => t8('Ordered purchase'), },
56
    # 'consume'       => { sub => sub { $::form->format_amount(\%::myconfig,$_[0]->consume,2); }, text => t8('Consume'), },
57
      );
58

  
59
#  for my $col (@sortable) {
60
#    $column_defs{$col}{link} = $self->self_url(
61
#      sort_by  => $col,
62
#      sort_dir => ($self->{sort_by} eq $col ? 1 - $self->{sort_dir} : $self->{sort_dir}),
63
#      page     => $self->{pages}{cur},
64
#      retype   => $::form->{retype},
65
#      stock    => $::form->{stock},
66
#    );
67
#  }
51
    partnumber  => {
52
      sub      => sub { $_[0]->partnumber },
53
      text     => t8('Part Number'),
54
      obj_link => sub { $_[0]->presenter->link_to },
55
    },
56
    description => {
57
      sub      => sub { $_[0]->description },
58
      text     => t8('Part Description'),
59
      obj_link => sub { $_[0]->presenter->link_to },
60
    },
61
    available   => {
62
      sub  => sub { $::form->format_amount(\%::myconfig,$_[0]->onhandqty,2); },
63
      text => t8('Available Stock'),
64
    },
65
    onhand      => {
66
      sub  => sub { $::form->format_amount(\%::myconfig,$_[0]->stockqty,2); },
67
      text => t8('Total Stock'),
68
    },
69
    rop         => {
70
      sub  => sub { $::form->format_amount(\%::myconfig,$_[0]->rop,2); },
71
      text => t8('Rop'),
72
    },
73
    ordered     => {
74
      sub => sub { $::form->format_amount(
75
                     \%::myconfig,$_[0]->get_open_ordered_qty,2); },
76
      text => t8('Ordered purchase'),
77
    },
78
  );
68 79

  
69 80
  map { $column_defs{$_}->{visible} = 1 } @visible;
70 81

  
......
82 93
  $report->set_options_from_form;
83 94

  
84 95
  unless ( $noshow ) {
85
    $self->models->disable_plugin('paginated') if $report->{options}{output_format} =~ /^(pdf|csv)$/i;
96
    if ($report->{options}{output_format} =~ /^(pdf|csv)$/i) {
97
      $self->models->disable_plugin('paginated');
98
    }
86 99
    $self->models->finalize; # for filter laundering
87
    $self->models->set_report_generator_sort_options(report => $report, sortable_columns => \@sortable);
88
    #$report->set_sort_indicator($self->{sort_by}, $self->{sort_dir});
100
    $self->models->set_report_generator_sort_options(
101
      report => $report, sortable_columns => \@sortable
102
    );
89 103
  }
90
  #$::request->layout->add_javascripts('kivi.PartsWarehouse.js');
91
  #$::form->get_lists('warehouses' => { 'key'    => 'WAREHOUSES',
92
  #                                     'bins'   => 'BINS', });
93 104
  my $parts = $self->_get_parts(0);
94 105
  my $top    = $self->render('disposition_manager/list_parts', { output => 0 },
95 106
                             noshow => $noshow,
96 107
                             PARTS => $parts,
97 108
                           );
98
  my $bottom = $noshow ? undef : $self->render('reorder_level_list/report_bottom', { output => 0}, models => $self->models );
109
  my $bottom = $noshow ? undef : $self->render(
110
    'disposition_manager/reorder_level_list/report_bottom',
111
    { output => 0}, models => $self->models );
99 112
  $report->set_options(
100 113
    raw_top_info_text    => $top,
101 114
    raw_bottom_info_text => $bottom,
102 115
  );
103
  $main::lxdebug->leave_sub();
104 116
}
117

  
105 118
sub action_add_to_purchase_basket{
106
  my ( $self ) = @_;
119
  my ($self) = @_;
120

  
121
  my $employee = SL::DB::Manager::Employee->current;
107 122

  
108 123
  my $parts_to_add = delete($::form->{ids}) || [];
109 124
  foreach my $id (@{ $parts_to_add }) {
110
    my $part = SL::DB::Manager::Part->find_by(id => $id) or die "Can't find part with id: $id\n";
125
    my $part = SL::DB::Manager::Part->find_by(id => $id)
126
      or die "Can't find part with id: $id\n";
127
    my $needed_qty = $part->order_qty < ($part->rop - $part->onhandqty) ?
128
                       $part->rop - $part->onhandqty
129
                     : $part->order_qty;
111 130
    my $basket_part = SL::DB::PurchaseBasketItem->new(
112
      parts_id    => $part->id,
113
      qty         => $part->ordersize, # was ist wenn order_size < (rop-onhand) ist? sollte dann nicht (rop-onhand) genommen werden?
131
      part_id    => $part->id,
132
      qty        => $needed_qty,
133
      orderer_id => $employee->id,
114 134
    )->save;
115 135
 }
116 136
 $self->action_show_basket;
117

  
118 137
}
119 138

  
120 139
sub action_show_basket {
121
  my ( $self ) = @_;
140
  my ($self) = @_;
122 141

  
123
  $::request->{layout}->add_javascripts('kivi.DispositionManager.js', 'kivi.PartDetail.js');
124
  my $basket_items = SL::DB::Manager::PurchaseBasketItem->get_all( query => [ cleared => 'F' ],  with_objects => [ 'part', 'part.makemodels' ]);
142
  $::request->{layout}->add_javascripts(
143
    'kivi.DispositionManager.js', 'kivi.PartDetail.js'
144
  );
145
  my $basket_items = SL::DB::Manager::PurchaseBasketItem->get_all(
146
    query => [ cleared => 'F' ],
147
    with_objects => [ 'part', 'part.makemodels' ]
148
  );
125 149
  $self->_setup_show_basket_action_bar;
126
  $self->render('disposition_manager/show_purchase_basket', BASKET_ITEMS => $basket_items, title => "Purchase basket" );
150
  $self->render(
151
    'disposition_manager/show_purchase_basket',
152
    BASKET_ITEMS => $basket_items,
153
    title => t8('Purchase basket'),
154
  );
127 155
}
128 156

  
129 157
sub action_show_vendor_items {
130
  my ( $self ) = @_;
158
  my ($self) = @_;
131 159

  
132
  my $makemodels_parts = SL::DB::Manager::Part->get_all( query => [ 'makemodels.make' => $::form->{v_id}, 'makemodels.sortorder' => 1, '!id' => ['5599'], ], sort_by => 'onhand', with_objects => [ 'makemodels' ]);
133
  $self->render('disposition_manager/_show_vendor_parts', { layout => 0 }, MAKEMODEL_ITEMS => $makemodels_parts);
160
  my $makemodels_parts = SL::DB::Manager::Part->get_all(
161
    query => [
162
      'makemodels.make' => $::form->{v_id},
163
      'makemodels.sortorder' => 1,
164
    ],
165
    sort_by => 'onhand',
166
    with_objects => [ 'makemodels' ]
167
  );
168
  $self->render(
169
    'disposition_manager/_show_vendor_parts',
170
    { layout => 0 },
171
    MAKEMODEL_ITEMS => $makemodels_parts
172
  );
134 173
}
135 174

  
136 175
sub action_transfer_to_purchase_order {
137 176

  
138
  my ( $self ) = @_;
139
  require SL::DB::Order;
140
  # require SL::DB::OrderItem;
141
  require SL::DB::Vendor;
177
  my ($self) = @_;
142 178
  my @error_report;
143 179

  
144
  unless (($::form->{ids} && scalar @{ $::form->{ids}}) || ( $::form->{vendor_part_ids} && scalar @{ $::form->{vendor_part_ids}})) {
145
    die 'There are no items selected';
180
  my $basket_items_ids = $::form->{ids};
181
  my $vendor_items_ids = $::form->{vendor_part_ids};
182

  
183
  unless (($basket_items_ids && scalar @{ $basket_items_ids})
184
      || ( $vendor_items_ids && scalar @{ $vendor_items_ids}))
185
    {
186
    $self->js->flash('error', t8('There are no items selected'));
187
    return $self->js->render();
146 188
  }
147 189
  my $v_id =  $::form->{vendor_ids}->[0] ;
148 190

  
149 191
  my ($vendor, $employee);
150
  $vendor   = SL::DB::Manager::Vendor->find_by(id => $v_id) or die "Can't find vendor";
151
  $employee = SL::DB::Manager::Employee->current            or die "Can't find employee";
152

  
192
  $vendor   = SL::DB::Manager::Vendor->find_by(id => $v_id);
193
  $employee = SL::DB::Manager::Employee->current;
153 194

  
154
  my $basket_items;
155
  $basket_items = SL::DB::Manager::PurchaseBasketItem->get_all( query => [ id => \@{ $::form->{ids} } ] ) if ($::form->{ids} && scalar @{ $::form->{ids}});
156 195

  
157
  my $vendor_items;
158
  $vendor_items = SL::DB::Manager::Part->get_all( query => [ id => \@{ $::form->{vendor_part_ids} } ] ) if ($::form->{vendor_part_ids} && scalar @{ $::form->{vendor_part_ids}});
196
  my @orderitem_maps = (); # part, qty, orderer_id
197
  if ($basket_items_ids && scalar @{ $basket_items_ids}) {
198
    my $basket_items = SL::DB::Manager::PurchaseBasketItem->get_all(
199
      query => [ id => $basket_items_ids ],
200
      with_objects => ['part'],
201
    );
202
    push @orderitem_maps, map {{
203
        part       => $_->part,
204
        qty        => $_->qty,
205
        orderer_id => $_->orderer_id,
206
      }} @{$basket_items};
207
  }
208
  if ($vendor_items_ids && scalar @{ $vendor_items_ids}) {
209
    my $vendor_items = SL::DB::Manager::Part->get_all(
210
      query => [ id => $vendor_items_ids ] );
211
    push @orderitem_maps, map {{
212
        part       => $_,
213
        qty        => $_->order_qty || 1,
214
        orderer_id => $employee->id,
215
      }} @{$vendor_items};
216
  }
159 217

  
160
  # create order first so we have a record for PriceSource
161 218
  my $order = SL::DB::Order->new(
162 219
    vendor_id               => $vendor->id,
163 220
    employee_id             => $employee->id,
......
170 227
    transdate               => DateTime->today_local
171 228
  );
172 229

  
230
  my @order_items;
173 231
  my $i = 0;
174
  my @items;
175

  
176
  if ($::form->{ids} && scalar @{ $::form->{ids}}) {
177
    foreach my $basket_item ( @{ $basket_items } ) {
178
      $i++;
179

  
180
      my $current_order_item = SL::DB::OrderItem->new(
181
        part                => $basket_item->part,
182
        qty                 => $basket_item->part->min_order_qty || 1,
183
        unit                => $basket_item->part->unit,
184
        description         => $basket_item->part->description,
185
        longdescription => $basket_item->part->notes,
186
        position            => $i,
187
        orderer_id          => $basket_item->orderer->id,
188
        price_factor_id => $basket_item->part->price_factor_id,
189
        price_factor    => $basket_item->part->price_factor_id ? $basket_item->part->price_factor->factor : '',
190
        position        => $i,
191
      );
192

  
193
      my $price_source  = SL::PriceSource->new(record_item => $current_order_item, record => $order);
194

  
195
      $current_order_item->sellprice($price_source->best_price ? $price_source->best_price->price : 0);
196
      $current_order_item->active_price_source($price_source->best_price ? $price_source->best_price->source : '');
197
      push(@items, $current_order_item);
198
    }
199
  }
232
  foreach my $orderitem_map (@orderitem_maps) {
233
    $i++;
234
    my $part = $orderitem_map->{part};
235
    my $qty = $orderitem_map->{qty};
236
    my $orderer_id = $orderitem_map->{orderer_id};
237

  
238
    my $order_item = SL::DB::OrderItem->new(
239
      part                => $part,
240
      qty                 => $qty,
241
      unit                => $part->unit,
242
      description         => $part->description,
243
      price_factor_id     => $part->price_factor_id,
244
      price_factor        =>
245
        $part->price_factor_id ? $part->price_factor->factor
246
                               : '',
247
      orderer_id          => $orderer_id,
248
      position            => $i,
249
    );
200 250

  
201
  if ($::form->{vendor_part_ids} && scalar @{ $::form->{vendor_part_ids}}) {
202
    foreach my $vendor_item ( @{ $vendor_items } ) {
203
      $i++;
204

  
205
      my $current_order_item = SL::DB::OrderItem->new(
206
        part                => $vendor_item,
207
        qty                 => $vendor_item->ordersize || 1,
208
        unit                => $vendor_item->unit,
209
        description         => $vendor_item->description,
210
        price_factor_id     => $vendor_item->price_factor_id,
211
        price_factor        => $vendor_item->price_factor_id ? $vendor_item->price_factor->factor : '',
212
        position            => $i,
213
        orderer_id          => $employee->id,
214
      );
215

  
216
      my $price_source  = SL::PriceSource->new(record_item => $current_order_item, record => $order);
217
      $current_order_item->sellprice($price_source->best_price->price);
218
      $current_order_item->active_price_source($price_source->best_price->source);
219
      push(@items, $current_order_item);
220
    }
251
    my $price_source  = SL::PriceSource->new(
252
      record_item => $order_item, record => $order);
253
    $order_item->sellprice(
254
      $price_source->best_price ? $price_source->best_price->price
255
                                : 0);
256
    $order_item->active_price_source(
257
      $price_source->best_price ? $price_source->best_price->source
258
                                : '');
259
    push @order_items, $order_item;
221 260
  }
222 261

  
223
  $order->orderitems( [ @items ] );
262
  $order->add_items( @order_items );
224 263

  
225 264
  $order->db->with_transaction( sub {
226 265
    $order->calculate_prices_and_taxes;
......
239 278
      $item->{custom_variables} = \@{ $item->cvars_by_config };
240 279
      $item->save;
241 280
    }
242
    SL::DB::Manager::PurchaseBasketItem->delete_all( where => [ id => \@{ $::form->{ids} }]) if ($::form->{ids} && scalar @{ $::form->{ids}});
281
    if ($basket_items_ids && scalar @{ $basket_items_ids}) {
282
      SL::DB::Manager::PurchaseBasketItem->delete_all(
283
        where => [ id => $basket_items_ids]);
284
    }
243 285
    return 1;
244 286
  }) || die "error: " . $order->db->error;
245 287

  
246
  my $user_prefs_doc = SL::Helper::UserPreferences->new(
247
    namespace         => 'DefaultOrderController',
288
  $self->redirect_to(
289
    controller => 'Order',
290
    action     => 'edit',
291
    type       => 'purchase_order',
292
    vc         => 'vendor',
293
    id         => $order->id,
248 294
  );
249
  my $doc_val = $::instance_conf->get_feature_experimental_order ? 'new' : 'old';
250
  $doc_val = $user_prefs_doc->get('default_order_controller') if ($user_prefs_doc);
251
  if ( $doc_val eq 'old') {
252
    $self->redirect_to(
253
      controller => 'oe.pl',
254
      action     => 'edit',
255
      type       => 'purchase_order',
256
      vc         => 'vendor',
257
      id         => $order->id,
258
    );
259
  } else {
260
    $self->redirect_to(
261
      controller => 'Order',
262
      action     => 'edit',
263
      type       => 'purchase_order',
264
      vc         => 'vendor',
265
      id         => $order->id,
266
    );
267
  }
268 295
}
269 296

  
270 297
sub _get_parts {
271 298
  my ($self, $ordered) = @_;
272 299

  
273 300
  my $query = <<SQL;
274
SELECT *
275
  FROM parts
276
 WHERE onhand < rop
277
   AND rop != 0
278
   AND id NOT IN( SELECT parts_id FROM purchase_basket_items )
279
   AND NOT obsolete
280
   ORDER BY partnumber
301
 WITH available AS (
302
   SELECT inv.parts_id, sum(qty) as sum
303
   FROM inventory inv
304
   LEFT JOIN warehouse w ON inv.warehouse_id = w.id
305
   WHERE NOT w.invalid
306
   GROUP BY inv.parts_id
307
 
308
   UNION ALL
309
 
310
   SELECT p.id, 0 as sum
311
   FROM parts p
312
   WHERE p.id NOT IN ( SELECT distinct parts_id from inventory)
313
     AND NOT p.obsolete
314
     AND p.rop != 0
315
 )
316

  
317
 SELECT p.id
318
 FROM parts p
319
 LEFT JOIN available ava ON ava.parts_id = p.id
320
 WHERE ( ava.sum < p.rop )
321
   AND p.id NOT IN ( SELECT part_id FROM purchase_basket_items )
322
   AND NOT p.obsolete
323
 ORDER BY p.partnumber
281 324
SQL
282

  
283
  my $parts = SL::DB::Manager::Part->get_objects_from_sql( sql => $query );
325
  my @ids = selectall_array_query($::form, $::form->get_standard_dbh, $query);
326
  return unless scalar @ids;
327
  my $parts = SL::DB::Manager::Part->get_all( query => [ id => \@ids ] );
284 328
  my $parts_to_order = [ grep { !$_->get_open_ordered_qty } @{$parts} ];
285
  my $parts_ordered = [ map { $_->id } grep { $_->get_open_ordered_qty } @{$parts} ];
286 329
  return $parts_to_order if !$ordered;
330
  my $parts_ordered = [
331
    map { $_->id } grep { $_->get_open_ordered_qty } @{$parts}
332
  ];
287 333
  return $parts_ordered if $ordered;
288 334
};
289 335

  
......
296 342
    model => 'Part',
297 343
    sorted => {
298 344
      _default => {
299
        by    => 'partnumber',
300
        dir   => 1,
345
        by  => 'partnumber',
346
        dir => 1,
301 347
      },
302
      partnumber   => $::locale->text('Part Number'),
303
      description   => $::locale->text('Description'),
348
      partnumber  => $::locale->text('Part Number'),
349
      description => $::locale->text('Description'),
304 350
     },
305
    query        => [
306
      (id            => \@parts) x !!@parts,
351
    query => [
352
      (id => \@parts) x !!@parts,
307 353
    ],
308 354
    paginated => {
309 355
      form_params => [ qw(page per_page) ],
......
321 367
    $bar->add(
322 368
      action => [
323 369
        t8('Action'),
324
        submit   => [ '#form', { action => "DispositionManager/add_to_purchase_basket" } ],
325
        tooltip  => t8('Add to purchase basket'),
370
        submit  => [
371
          '#form', { action => "DispositionManager/add_to_purchase_basket" } ],
372
        tooltip => t8('Add to purchase basket'),
326 373
      ],
327 374
    );
328 375
  }
......
334 381
    $bar->add(
335 382
      action => [
336 383
        t8('Reload'),
337
        link => $self->url_for(controller => 'DispositionManager', action => 'show_basket'),
384
        link => $self->url_for(
385
          controller => 'DispositionManager',
386
          action     => 'show_basket',
387
        ),
338 388
      ],
339 389
      action => [
340 390
        t8('Action'),
341
        call   => [ 'kivi.DispositionManager.create_order' ],
342
        tooltip  => t8('Create purchase order'),
391
        call    => [ 'kivi.DispositionManager.create_order' ],
392
        tooltip => t8('Create purchase order'),
343 393
      ],
344 394
    );
345 395
  }
SL/Controller/Part.pm
263 263

  
264 264
  if ( !$self->_is_in_purchase_basket && scalar @{$self->part->makemodels}) {
265 265
    my $basket_part = SL::DB::PurchaseBasketItem->new(
266
      parts_id    => $self->part->id,
267
      qty         => $self->part->ordersize || 1, # was ist wenn order_size < (rop-onhand) ist? sollte dann nicht (rop-onhand) genommen werden?
266
      part_id     => $self->part->id,
267
      qty         => $self->part->order_qty || 1, # was ist wenn order_qty < (rop-onhand) ist? sollte dann nicht (rop-onhand) genommen werden?
268 268
      orderer     => SL::DB::Manager::Employee->current,
269 269
    )->save;
270 270

  
......
1481 1481
sub _is_in_purchase_basket {
1482 1482
  my ( $self ) = @_;
1483 1483

  
1484
  return SL::DB::Manager::PurchaseBasketItem->get_all_count( query => [ parts_id => $self->part->id ] );
1484
  return SL::DB::Manager::PurchaseBasketItem->get_all_count( query => [ part_id => $self->part->id ] );
1485 1485
}
1486 1486
sub _is_ordered {
1487 1487
  my ( $self ) = @_;
SL/DB/Manager/Part.pm
98 98

  
99 99
  my $query = <<SQL;
100 100
WITH
101
open_qty AS ( SELECT parts_id, sum(oi.qty) as sum
102
FROM orderitems oi
103
LEFT OUTER JOIN oe o ON (oi.trans_id = o.id)
104
WHERE
105
oi.parts_id = ?
106
AND (NOT COALESCE(o.quotation, FALSE))
107
AND (NOT COALESCE(o.closed,    FALSE))
108
AND (NOT COALESCE(o.delivered, FALSE))
109
AND (COALESCE(o.vendor_id, 0) <> 0)
110
GROUP BY oi.parts_id),
111

  
112
open_orderitems_ids AS ( SELECT oi.id, parts_id
113
FROM orderitems oi
114
LEFT OUTER JOIN oe o ON (oi.trans_id = o.id)
115
WHERE
116
oi.parts_id = ?
117
AND (NOT COALESCE(o.quotation, FALSE))
118
AND (NOT COALESCE(o.closed,    FALSE))
119
AND (NOT COALESCE(o.delivered, FALSE))
120
AND (COALESCE(o.vendor_id, 0) <> 0)
101
open_qty AS (
102
  SELECT parts_id, sum(oi.qty) as sum
103
  FROM orderitems oi
104
  LEFT OUTER JOIN oe o ON (oi.trans_id = o.id)
105
  WHERE
106
    oi.parts_id = ?
107
    AND (NOT COALESCE(o.quotation, FALSE))
108
    AND (NOT COALESCE(o.closed,    FALSE))
109
    AND (NOT COALESCE(o.delivered, FALSE))
110
    AND (COALESCE(o.vendor_id, 0) <> 0)
111
  GROUP BY oi.parts_id
121 112
),
122 113

  
123
delivered_qty AS ( select parts_id, sum(qty) AS sum FROM delivery_order_items
124
WHERE id IN (select to_id from record_links WHERE from_id IN (SELECT id FROM open_orderitems_ids) AND from_table = 'orderitems' AND to_table = 'delivery_order_items') AND parts_id = ? GROUP BY parts_id),
114
open_orderitems_ids AS (
115
  SELECT oi.id, parts_id
116
  FROM orderitems oi
117
  LEFT OUTER JOIN oe o ON (oi.trans_id = o.id)
118
  WHERE
119
    oi.parts_id = ?
120
    AND (NOT COALESCE(o.quotation, FALSE))
121
    AND (NOT COALESCE(o.closed,    FALSE))
122
    AND (NOT COALESCE(o.delivered, FALSE))
123
    AND (o.vendor_id is not null)
124
),
125

  
126
delivered_qty AS (
127
  SELECT parts_id, sum(qty) AS sum
128
  FROM delivery_order_items
129
  WHERE id IN (
130
    SELECT to_id from record_links
131
    WHERE
132
      from_id IN ( SELECT id FROM open_orderitems_ids)
133
      AND from_table = 'orderitems'
134
      AND to_table = 'delivery_order_items'
135
  ) AND parts_id = ?
136
  GROUP BY parts_id
137
),
125 138

  
126
open_ordered_qty AS ( select oq.parts_id, oq.sum AS ordered_sum, iif(dq.sum is null,0.00,dq.sum) AS sum, sum(iif(oq.sum is null,0.00,oq.sum) - iif(dq.sum is null,0.00,dq.sum)) AS open_qty
127
FROM open_qty oq
128
LEFT JOIN delivered_qty dq ON dq.parts_id = oq.parts_id
129
GROUP BY oq.parts_id, oq.sum, dq.sum)
139
open_ordered_qty AS (
140
  SELECT
141
    oq.parts_id,
142
    oq.sum AS ordered_sum,
143
    COALESCE(dq.sum,0.00) AS sum,
144
    sum(COALESCE(oq.sum,0.00) - COALESCE(dq.sum,0.00)) AS open_qty
145
  FROM open_qty oq
146
  LEFT JOIN delivered_qty dq ON dq.parts_id = oq.parts_id
147
  GROUP BY oq.parts_id, oq.sum, dq.sum
148
)
130 149

  
131 150
SELECT open_qty FROM open_ordered_qty
132 151

  
133 152
SQL
134 153

  
135
  my ($open_qty) = selectrow_query($::form, $class->object_class->init_db->dbh, $query, $part_id, $part_id, $part_id);
136
### Über Rose dauert das ganze 3 minuten
137
#    my $openitems = SL::DB::Manager::OrderItem->get_all(where => [
138
#                                                            parts_id => $part_id,
139
#                                                            'order.closed' => 0,
140
#                                                            'order.quotation' => 0,
141
#                                                            'order.delivered' => 0,
142
#                                                            #  'order.type' => { ilike => 'purchase_order' },
143
#                                                            'order.vendor_id' => { ne => 0 }
144
#                                                          ],
145
#                                                           with_objects => ['order'],
146
#                                                         );
147
#                                                         $main::lxdebug->dump(0, 'WH:OPEN ', $openitems);
148

  
149
#  return 0 unless $openitems;
150
#  my $open_qty        = 0;
151
#  my $ordered_qty     = 0;
152
#  my $delivered_qty   = 0;
153
#  for my $openitem (@{ $openitems }) {
154
##      if($openitem->order->type eq 'purchase_order') {
155
#        $ordered_qty   += $openitem->qty;
156
#        $delivered_qty += $openitem->delivered_qty;
157
##      }
158
#    }
159
##   $open_qty = map{ $_->qty - $_->delivered_qty } @{$openitems};
160
#      $open_qty = $ordered_qty - $delivered_qty;
161

  
162
#  return $open_qty;
154
  my ($open_qty) = selectrow_query(
155
    $::form, $class->object_class->init_db->dbh,
156
    $query, $part_id, $part_id, $part_id
157
  );
163 158

  
164 159
  return $open_qty if $open_qty > 0;
165 160
  return 0;
SL/DB/MetaSetup/MakeModel.pm
14 14
  lastcost         => { type => 'numeric', precision => 15, scale => 5 },
15 15
  lastupdate       => { type => 'date' },
16 16
  make             => { type => 'integer' },
17
  min_order_qty => { type => 'numeric', precision => 15, scale => 5 },
18 17
  model            => { type => 'text' },
19 18
  mtime            => { type => 'timestamp' },
20 19
  part_description => { type => 'text' },
SL/DB/MetaSetup/Part.pm
31 31
  notes              => { type => 'text' },
32 32
  obsolete           => { type => 'boolean', default => 'false', not_null => 1 },
33 33
  onhand             => { type => 'numeric', default => '0', precision => 25, scale => 5 },
34
  order_qty          => { type => 'numeric', default => '0', not_null => 1, precision => 15, scale => 5 },
34 35
  part_type          => { type => 'enum', check_in => [ 'part', 'service', 'assembly', 'assortment' ], db_type => 'part_type_enum', not_null => 1 },
35 36
  partnumber         => { type => 'text', not_null => 1 },
36 37
  partsgroup_id      => { type => 'integer' },
SL/DB/MetaSetup/PurchaseBasketItem.pm
11 11
__PACKAGE__->meta->columns(
12 12
  cleared    => { type => 'boolean', default => 'false', not_null => 1 },
13 13
  id         => { type => 'serial', not_null => 1 },
14
  itime      => { type => 'timestamp', default => 'now()' },
15
  mtime      => { type => 'timestamp' },
14 16
  orderer_id => { type => 'integer' },
15
  parts_id   => { type => 'integer' },
16
  qty        => { type => 'numeric', precision => 15, scale => 5 },
17
  part_id    => { type => 'integer' },
18
  qty        => { type => 'numeric', not_null => 1, precision => 15, scale => 5 },
17 19
);
18 20

  
19 21
__PACKAGE__->meta->primary_key_columns([ 'id' ]);
20 22

  
23
__PACKAGE__->meta->allow_inline_column_values(1);
24

  
21 25
__PACKAGE__->meta->foreign_keys(
22 26
  orderer => {
23 27
    class       => 'SL::DB::Employee',
......
26 30

  
27 31
  part => {
28 32
    class       => 'SL::DB::Part',
29
    key_columns => { parts_id => 'id' },
33
    key_columns => { part_id => 'id' },
30 34
  },
31 35
);
32 36

  
SL/DB/Part.pm
8 8
use Rose::DB::Object::Helpers qw(as_tree);
9 9

  
10 10
use SL::Locale::String qw(t8);
11
use SL::Helper::Inventory;
11 12
use SL::DBUtils;
12 13
use SL::DB::MetaSetup::Part;
13 14
use SL::DB::Manager::Part;
......
97 98
__PACKAGE__->before_save('_before_save_set_partnumber');
98 99
__PACKAGE__->before_save('_before_save_set_assembly_weight');
99 100

  
100
sub onhandqty_as_number {
101
  my ($self, $string) = @_;
102
  $self->onhandqty($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
103
  return $::form->format_amount(\%::myconfig, $self->onhandqty, 2);
104
}
105

  
106 101
sub init_onhandqty{
107 102
  my ($self) = @_;
108 103
  my $qty = SL::Helper::Inventory::get_onhand(part => $self->id);
109 104
  return $qty;
110 105
}
111 106

  
112
sub stockqty_as_number {
113
  my ($self, $string) = @_;
114
  $self->stockqty($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
115
  return $::form->format_amount(\%::myconfig, $self->stockqty, 2);
116
}
117

  
118 107
sub init_stockqty{
119 108
  my ($self) = @_;
120 109
  my $qty = SL::Helper::Inventory::get_stock(part => $self->id);
121 110
  return $qty;
122 111
}
123 112

  
124
sub get_open_ordered_qty_as_number {
125
  my ($self, $string) = @_;
126
  $self->get_open_ordered_qty($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
127
  return $::form->format_amount(\%::myconfig, $self->get_open_ordered_qty, 2);
128
}
129

  
130 113
sub init_get_open_ordered_qty {
131 114
  my $self   = shift;
132 115
  my $result = SL::DB::Manager::Part->get_open_ordered_qty($self->id);
133 116

  
134 117
  return $result;
135 118
}
136
#sub reservedqty_as_number {
137
#  my ($self, $string) = @_;
138
#  $self->reservedqty($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
139
#  return $::form->format_amount(\%::myconfig, $self->reservedqty, $self->places);
140
#}
141

  
142
#sub init_onhandqty{
143
#  my ($self) = @_;
144
#  my $qty = get_onhand(part => $self->id);
145
#  return $qty;
146
#}
147
>>>>>>> cf3ff322d1 (Einkaufshelfer/Dispositionsmanager bescheleunigt)
148 119

  
149 120
sub _before_save_set_partnumber {
150 121
  my ($self) = @_;
......
636 607
}
637 608

  
638 609
sub vendor_dropdown {
639
  my ( $self ) = @_;
610
  my ($self) = @_;
640 611

  
641 612
  my @vendor_dd;
642
  # $main::lxdebug->dump(0, 'WH:MakeModels ', $_[0]->makemodels);
643 613

  
644
  foreach my $mm ( @{$_[0]->makemodels} ){
614
  foreach my $mm ( @{$self->makemodels} ){
645 615
    my $vendor = SL::DB::Manager::Vendor->get_first( where => [ id => $mm->make ] );
646 616
    my @tmp = ({ title => $vendor->vendornumber . "--" .$vendor->name . " (" . $::form->format_amount(\%::myconfig, $mm->lastcost, 2) . ")", value => $vendor->{id} });
647 617
    push @vendor_dd, @tmp;
js/locale/de.js
47 47
"Delete requirement spec":"Pflichtenheft löschen",
48 48
"Delete template":"Vorlage löschen",
49 49
"Delete text block":"Textblock löschen",
50
"Details of article number \"#1\"":"Details von Artikel \"#1\"",
50 51
"Do you really want to cancel?":"Möchten Sie wirklich abbrechen?",
51 52
"Do you really want to continue?":"Möchten Sie wirklich fortfahren?",
52 53
"Do you really want to delete the selected documents?":"Möchten Sie wirklich diese Dateien löschen?",
js/locale/en.js
47 47
"Delete requirement spec":"",
48 48
"Delete template":"",
49 49
"Delete text block":"",
50
"Details of article number \"#1\"":"",
50 51
"Do you really want to cancel?":"",
51 52
"Do you really want to continue?":"",
52 53
"Do you really want to delete the selected documents?":"",
locale/de/all
306 306
  'All general ledger entries'  => 'Alle Hauptbucheinträge',
307 307
  'All groups'                  => 'Alle Gruppen',
308 308
  'All modules'                 => 'Alle Module',
309
  'All parts of vendor odered by onhand' => 'Alle Artikel des Lieferanten sortiert nach Lagerbestand',
309 310
  'All partsgroups'             => 'Alle Warengruppen',
310 311
  'All pay postings successfully imported.' => 'Alle Lohnbuchungen erfolgreich importiert.',
311 312
  'All payments have already been posted.' => 'Es wurden bereits alle Zahlungen verbucht.',
......
329 330
  'Already counted'             => 'Bereits erfasst',
330 331
  'Already imported entries (duplicates)' => 'Bereits importierte Einträge (Duplikate)',
331 332
  'Already imported: '          => 'Bereits importiert:',
332
  'Also with reserved Qty'      => 'inklusive Reservierungen',
333 333
  'Always edit assembly items (user can change/delete items even if assemblies are already produced)' => 'Erzeugnisbestandteile verändern (Löschen/Umsortieren) auch nachdem dieses Erzeugnis schon produziert wurde.',
334 334
  'Always edit assortment items (user can change/delete items even if assortments are already used)' => 'Sortimentsbestandteile verändern (Löschen/Umsortieren), auch nachdem dieses Sortiment schon verwendet wurde.',
335 335
  'Always save orders with a projectnumber (create new projects)' => 'Aufträge immer mit Projektnummer speichern (neue Projekte erstellen)',
......
384 384
  'Article Code'                => 'Artikelkürzel',
385 385
  'Article classification'      => 'Artikel-Klassifizierung',
386 386
  'Article data'                => 'Artikeldaten',
387
  'Article details'             => 'Artikeldetails',
387 388
  'Article type'                => 'Artikeltyp',
388 389
  'Articles'                    => 'Artikel',
389 390
  'As a result, the saved onhand values of the present goods can be stored into a warehouse designated by you, or will be reset for a proper warehouse tracking' => 'Als Konsequenz können die gespeicherten Mengen entweder in ein Lager überführt werden, oder für eine frische Lagerverwaltung resettet werden.',
......
444 445
  'Automatically created invoice for fee and interest for dunning %s' => 'Automatisch erzeugte Rechnung für Gebühren und Zinsen zu Mahnung %s',
445 446
  'Available'                   => 'Verfügbar',
446 447
  'Available Prices'            => 'Mögliche Preise',
448
  'Available Stock'             => 'verfügbarer Bestand',
447 449
  'Available qty'               => 'Lagerbestand',
448 450
  'Available to all users'      => 'Für alle BenutzerInnen verfügbar',
449 451
  'BALANCE SHEET'               => 'BILANZ',
......
1196 1198
  'Detailed information about this customer' => 'Detaillierte Informationen über diesen Kunden',
1197 1199
  'Details'                     => 'Details',
1198 1200
  'Details (one letter abbreviation)' => 'D',
1201
  'Details of article number "#1"' => 'Details von Artikel "#1"',
1199 1202
  'Details: #1'                 => 'Details: #1',
1200 1203
  'Developer Tools'             => 'Developer Tools',
1201 1204
  'Dial command missing in kivitendo configuration\'s [cti] section' => 'Wählbefehl fehlt im Abschnitt [cti] der kivitendo-Konfiguration',
......
1216 1219
  'Display in basic data tab'   => 'Im Reiter Basisdaten anzeigen',
1217 1220
  'Display options'             => 'Anzeigeoptionen',
1218 1221
  'Displayable Name Preferences' => 'Einstellungen für Anzeigenamen',
1219
  'Disposition manager'         => 'Dispositionsmanager',
1220 1222
  'Do not change the tax rate of taxkey 0.' => 'Ändern Sie nicht den Steuersatz vom Steuerschlüssel 0.',
1221 1223
  'Do not check for duplicates' => 'Nicht nach Dubletten suchen',
1222 1224
  'Do not create Factur-X/ZUGFeRD invoices' => 'Keine Factur-X-/ZUGFeRD-Rechnungen erzeugen',
......
2584 2586
  'Order Date missing!'         => 'Auftragsdatum fehlt!',
2585 2587
  'Order Number'                => 'Auftragsnummer',
2586 2588
  'Order Number missing!'       => 'Auftragsnummer fehlt!',
2589
  'Order Size'                  => 'Bestellmenge',
2587 2590
  'Order amount'                => 'Auftragswert',
2588 2591
  'Order data'                  => 'Auftragsinformationen',
2589 2592
  'Order deleted!'              => 'Auftrag gelöscht!',
......
2595 2598
  'Order/Item row name'         => 'Name der Auftrag-/Positions-Zeilen',
2596 2599
  'Order/Item/Stock row name'   => 'Name der Auftrag-/Positions-/Lager-Zeilen',
2597 2600
  'Order/RFQ Number'            => 'Belegnummer',
2598
  'Order:'                      => 'Auftrag:',
2599 2601
  'OrderItem'                   => 'Position',
2600 2602
  'Ordered'                     => 'Von Kunden bestellt',
2601
  'Ordered qty'                 => 'bestellt',
2602 2603
  'Ordered purchase'            => 'Bestellte Menge',
2603 2604
  'Orderer'                     => 'BestellerIn',
2604 2605
  'Orders'                      => 'Aufträge',
......
2671 2672
  'Part Type'                   => 'Artikel-Typ',
2672 2673
  'Part Unit'                   => 'Einheit',
2673 2674
  'Part added to purchasebasket' => 'Zu Einkaufkorb hinzugefügt',
2674
  'Part already in purchasebasket or has no vendor' => 'Artikel ist schon im Bestellwarenkorb oder hat keinen Lieferanten',
2675 2675
  'Part already in purchasebasket' => 'Schon im Einkaufskorb',
2676
  'Part already in purchasebasket or has no vendor' => 'Artikel ist schon im Bestellwarenkorb oder hat keinen Lieferanten',
2676 2677
  'Part already ordered'        => 'Artikel ist bestellt',
2677 2678
  'Part classifications'        => 'Artikel-Klassifizierungen',
2678 2679
  'Part marked as "Shop part"'  => 'Markiert als Shopartikel',
......
2688 2689
  'Parts Classification'        => 'Artikel-Klassifizierung',
2689 2690
  'Parts Inventory'             => 'Warenliste',
2690 2691
  'Parts Master Data'           => 'Artikelstammdaten',
2691
  'Parts short onhand'          => 'Artikel mit geringer Lagermenge',
2692 2692
  'Parts with existing part numbers' => 'Artikel mit existierender Artikelnummer',
2693 2693
  'Parts, services and assemblies' => 'Waren, Dienstleistungen und Erzeugnisse',
2694 2694
  'Partsgroup'                  => 'Warengruppe',
......
3119 3119
  'Rename Images'               => 'Bilder umbenennen',
3120 3120
  'Rename attachment'           => 'Dateianhang umbenennen',
3121 3121
  'Renumber sections and function blocks' => 'Abschnitte/Funktionsblöcke neu nummerieren',
3122
  'Reorder Level List'          => 'Meldebestandsliste',
3122 3123
  'Replace legacy order controller with new one' => 'Neuen Auftrags-Controller verwenden',
3123 3124
  'Replace the orphaned currencies by other not orphaned currencies. To do so, please delete the currency in the textfields above and replace it by another currency. You could loose or change unintentionally exchangerates. Go on very carefully since you could destroy transactions.' => 'Ersetze die Währungen durch andere gültige Währungen. Wenn Sie sich hierfür entscheiden, ersetzen Sie bitte alle Währungen, die oben angegeben sind, durch Währungen, die in Ihrem System ordnungsgemäß eingetragen sind. Alle eingetragenen Wechselkurse für die verwaiste Währung werden dabei gelöscht. Bitte gehen Sie sehr vorsichtig vor, denn die betroffenen Buchungen können unter Umständen kaputt gehen.',
3124 3125
  'Report Positions'            => 'Berichte',
......
3464 3465
  'Shoporders'                  => 'Shopbest.',
3465 3466
  'Shops'                       => 'Webshops',
3466 3467
  'Short'                       => 'Knapp',
3467
  'Short onhand Ordered'        => 'Geringe Lagermenge, aber bestellt',
3468 3468
  'Short onhand'                => 'Geringe Lagermenge',
3469 3469
  'Should VAT ID or taxnumber be unique for all vendors? This is checked when saving a vendor\'s master data. One of the fields is sufficient and required.' => 'Soll die UStID oder Steuernummer eindeutig über alle Lieferanten sein? Dies wird beim Speichern von Lieferantenstammdaten geprüft. Eines dieser Felder reicht aus und muss angegeben sein.',
3470 3470
  'Should VAT ID or taxnumber be unique for customers? This is checked when saving a customer\'s master data. One of the fields is sufficient and required.' => 'Soll die UStID oder Steuernummer eindeutig über alle Kunden sein? Dies wird beim Speichern von Kundenstammdaten geprüft. Eines dieser Felder reicht aus und muss angegeben sein.',
......
4140 4140
  'There are no entries that match the filter.' => 'Es gibt keine Einträge, auf die der Filter zutrifft.',
4141 4141
  'There are no items in stock.' => 'Dieser Artikel ist nicht eingelagert.',
4142 4142
  'There are no items on your TODO list at the moment.' => 'Ihre Aufgabenliste enthält momentan keine Einträge.',
4143
  'There are no items selected' => 'Es wurden keine Positionen ausgewählt',
4143 4144
  'There are no orders for the selected time period.' => 'Es gibt für den ausgewählten Zeitraum keine Aufträge.',
4144 4145
  'There are no record templates yet.' => 'Es gibt noch keine Belegvorlagen.',
4145 4146
  'There are parts with no reclamation reason at position:' => 'Es git Artikel mit keinem Reklamationsgrund an Position:',
......
4300 4301
  'Total'                       => 'Summe',
4301 4302
  'Total Fees'                  => 'Kumulierte Gebühren',
4302 4303
  'Total Sales Orders Value'    => 'Auftragseingang',
4304
  'Total Stock'                 => 'Gesamtbestand',
4303 4305
  'Total number of entries'     => 'Gesamtzahl Einträge',
4304 4306
  'Total stock value'           => 'Gesamter Bestandswert',
4305 4307
  'Total sum'                   => 'Gesamtsumme',
......
4513 4515
  'VAT ID'                      => 'USt-IdNr.',
4514 4516
  'VAT ID and/or taxnumber must be given.' => 'UStId und/oder Steuernummer muss angegeben werden.',
4515 4517
  'VN'                          => 'Kred.-Nr.',
4516
  'VPE'                         => '',
4517 4518
  'Valid'                       => 'Gültig',
4518 4519
  'Valid are integer values and floating point numbers, e.g. 4.75h = 4 hours and 45 minutes.' => 'Erlaubt sind ganzzahlige Werte und Kommawerte: Beispiel: 4,75h = 4 Stunden und 45 Minuten.',
4519 4520
  'Valid for Purchase'          => 'Gültig für Einkauf',
......
4916 4917
  'once'                        => 'einmalig',
4917 4918
  'one time'                    => 'einmalig',
4918 4919
  'one-time execution'          => 'einmalige Ausführung',
4919
  'onhand'                      => 'Auf Lager',
4920 4920
  'only OB Transactions'        => 'nur EB-Buchungen',
4921 4921
  'open'                        => 'Offen',
4922 4922
  'order'                       => 'Reihenfolge',
......
4960 4960
  'revert deleted'              => 'löschen rückgängig',
4961 4961
  'rfq_list'                    => 'anfragenliste',
4962 4962
  'rma_delivery_order_list'     => 'lieferscheinliste_rma',
4963
  'rop'                         => 'Mindestlagerbestand',
4964 4963
  'running'                     => 'läuft',
4965 4964
  'sales tax identification number' => 'USt-IdNr.',
4966 4965
  'sales_delivery_order_list'   => 'lieferscheinliste_verkauf',
locale/en/all
272 272
  'Add text block'              => '',
273 273
  'Add time recording article'  => '',
274 274
  'Add title'                   => '',
275
  'Add to basket'               => '',
276
  'Add to purchase basket'      => '',
275 277
  'Add unit'                    => '',
276 278
  'Added sections and function blocks: #1' => '',
277 279
  'Added text blocks: #1'       => '',
......
304 306
  'All general ledger entries'  => '',
305 307
  'All groups'                  => '',
306 308
  'All modules'                 => '',
309
  'All parts of vendor odered by onhand' => '',
307 310
  'All partsgroups'             => '',
308 311
  'All pay postings successfully imported.' => '',
309 312
  'All payments have already been posted.' => '',
......
381 384
  'Article Code'                => '',
382 385
  'Article classification'      => '',
383 386
  'Article data'                => '',
387
  'Article details'             => '',
384 388
  'Article type'                => '',
385 389
  'Articles'                    => '',
386 390
  'As a result, the saved onhand values of the present goods can be stored into a warehouse designated by you, or will be reset for a proper warehouse tracking' => '',
......
441 445
  'Automatically created invoice for fee and interest for dunning %s' => '',
442 446
  'Available'                   => '',
443 447
  'Available Prices'            => '',
448
  'Available Stock'             => '',
444 449
  'Available qty'               => '',
445 450
  'Available to all users'      => '',
446 451
  'BALANCE SHEET'               => '',
......
890 895
  'Create new version'          => '',
891 896
  'Create one from the context menu by right-clicking on this text.' => '',
892 897
  'Create order'                => '',
898
  'Create purchase order'       => '',
893 899
  'Create sales invoices with Factur-X/ZUGFeRD data' => '',
894 900
  'Create sales invoices with Swiss QR-bill' => '',
895 901
  'Create tables'               => '',
......
1191 1197
  'Detailed information about this contact' => '',
1192 1198
  'Detailed information about this customer' => '',
1193 1199
  'Details'                     => '',
1194
  'Details (one letter abbreviation)' => '',
1200
  'Details (one letter abbreviation)' => 'D',
1201
  'Details of article number "#1"' => '',
1195 1202
  'Details: #1'                 => '',
1196 1203
  'Developer Tools'             => '',
1197 1204
  'Dial command missing in kivitendo configuration\'s [cti] section' => '',
......
2173 2180
  'List of jobs'                => '',
2174 2181
  'List of tax zones'           => '',
2175 2182
  'List open SEPA exports'      => '',
2183
  'List short onhand'           => '',
2176 2184
  'List time recordings of all staff members' => '',
2177 2185
  'Listprice'                   => '',
2178 2186
  'Load'                        => '',
......
2273 2281
  'Microfiche'                  => '',
2274 2282
  'Minimize Panel'              => '',
2275 2283
  'Minimum Amount'              => '',
2284
  'Minimum order quantity'      => '',
2276 2285
  'Miscellaneous'               => '',
2277 2286
  'Missing \'description\' field.' => '',
2278 2287
  'Missing \'tag\' field.'      => '',
......
2372 2381
  'No Shopdescription'          => '',
2373 2382
  'No Shopimages'               => '',
2374 2383
  'No VAT Info for this Factur-X/ZUGFeRD invoice, please ask your vendor to add this for his Factur-X/ZUGFeRD data.' => '',
2384
  'No Vendor'                   => '',
2375 2385
  'No Vendor was found matching the search parameters.' => '',
2376 2386
  'No account selected. Please select an account.' => '',
2377 2387
  'No action defined.'          => '',
......
2460 2470
  'No valid number entered for pricegroup "#1".' => '',
2461 2471
  'No vendor has been selected yet.' => '',
2462 2472
  'No vendor selected or found!' => '',
2473
  'No vendors to add to purchasebasket' => '',
2463 2474
  'No warehouse has been created yet or the quantity of the bins is not configured yet.' => '',
2464 2475
  'No webdav backend enabled.'  => '',
2465 2476
  'No year given for method year' => '',
......
2534 2545
  'One of the columns "qty" or "target_qty" must be given. If "target_qty" is given, the quantity to transfer for each transfer will be calculate, so that the quantity for this part, warehouse and bin will result in the given "target_qty" after each transfer.' => '',
2535 2546
  'One of the units used (#1) cannot be mapped to a known unit code from the UN/ECE Recommendation 20 list.' => '',
2536 2547
  'One or more Perl modules missing' => '',
2548
  'Onhand'                      => '',
2537 2549
  'Onhand only sets the quantity in master data, not in inventory. This is only a legacy info field and will be overwritten as soon as a inventory transfer happens.' => '',
2538 2550
  'Only Lines with Notes or Errors' => '',
2539 2551
  'Only Price'                  => '',
......
2573 2585
  'Order Date missing!'         => '',
2574 2586
  'Order Number'                => '',
2575 2587
  'Order Number missing!'       => '',
2588
  'Order Size'                  => '',
2576 2589
  'Order amount'                => '',
2577 2590
  'Order data'                  => '',
2578 2591
  'Order deleted!'              => '',
2579 2592
  'Order item search'           => '',
2580 2593
  'Order probability'           => '',
2581 2594
  'Order probability & expected billing date' => '',
2595
  'Order quantity'              => '',
2582 2596
  'Order value periodicity'     => '',
2583 2597
  'Order/Item row name'         => '',
2584 2598
  'Order/Item/Stock row name'   => '',
2585 2599
  'Order/RFQ Number'            => '',
2586 2600
  'OrderItem'                   => '',
2587 2601
  'Ordered'                     => '',
2602
  'Ordered purchase'            => '',
2603
  'Orderer'                     => '',
2588 2604
  'Orders'                      => '',
2589 2605
  'Orders / Delivery Orders deleteable' => '',
2590 2606
  'Orders in Webshop'           => '',
......
2654 2670
  'Part Test'                   => '',
2655 2671
  'Part Type'                   => '',
2656 2672
  'Part Unit'                   => '',
2673
  'Part added to purchasebasket' => '',
2674
  'Part already in purchasebasket' => '',
2675
  'Part already in purchasebasket or has no vendor' => '',
2676
  'Part already ordered'        => '',
2657 2677
  'Part classifications'        => '',
2658 2678
  'Part marked as "Shop part"'  => '',
2659 2679
  'Part picker'                 => '',
......
2952 2972
  'Purchase Reclamation'        => '',
2953 2973
  'Purchase Reclamations'       => '',
2954 2974
  'Purchase Reclamations deleteable' => '',
2975
  'Purchase basket'             => '',
2955 2976
  'Purchase delivery order'     => '',
2956 2977
  'Purchase invoices'           => '',
2957 2978
  'Purchase invoices changeable' => '',
......
3068 3089
  'Registration'                => '',
3069 3090
  'Relaxed (UTF-8)'             => '',
3070 3091
  'Release From Stock'          => '',
3092
  'Reload'                      => '',
3071 3093
  'Remaining'                   => '',
3072 3094
  'Remaining Amount'            => '',
3073 3095
  'Remaining Net Amount'        => '',
......
3096 3118
  'Rename Images'               => '',
3097 3119
  'Rename attachment'           => '',
3098 3120
  'Renumber sections and function blocks' => '',
3121
  'Reorder Level List'          => '',
3099 3122
  'Replace legacy order controller with new one' => '',
3100 3123
  'Replace the orphaned currencies by other not orphaned currencies. To do so, please delete the currency in the textfields above and replace it by another currency. You could loose or change unintentionally exchangerates. Go on very carefully since you could destroy transactions.' => '',
3101 3124
  'Report Positions'            => '',
......
3161 3184
  'Risk'                        => '',
3162 3185
  'Risk levels'                 => '',
3163 3186
  'Risks'                       => '',
3187
  'Rop'                         => '',
3164 3188
  'Rounding'                    => '',
3165 3189
  'Rounding Gain'               => '',
3166 3190
  'Rounding Loss'               => '',
......
3440 3464
  'Shoporders'                  => '',
3441 3465
  'Shops'                       => '',
3442 3466
  'Short'                       => '',
3467
  'Short onhand'                => '',
3443 3468
  'Should VAT ID or taxnumber be unique for all vendors? This is checked when saving a vendor\'s master data. One of the fields is sufficient and required.' => '',
3444 3469
  'Should VAT ID or taxnumber be unique for customers? This is checked when saving a customer\'s master data. One of the fields is sufficient and required.' => '',
3445 3470
  'Should ap transactions be and when should they be changeable or deleteable after posting?' => '',
......
3501 3526
  'Show overdue sales quotations and requests for quotations...' => '',
3502 3527
  'Show parts'                  => '',
3503 3528
  'Show parts longdescription (notes) in select list' => '',
3529
  'Show purchase basket'        => '',
3504 3530
  'Show purchase letters report' => '',
3505 3531
  'Show record tab in customer' => '',
3506 3532
  'Show record tab in vendor'   => '',
......
4112 4138
  'There are no entries that match the filter.' => '',
4113 4139
  'There are no items in stock.' => '',
4114 4140
  'There are no items on your TODO list at the moment.' => '',
4141
  'There are no items selected' => '',
4115 4142
  'There are no orders for the selected time period.' => '',
4116 4143
  'There are no record templates yet.' => '',
4117 4144
  'There are parts with no reclamation reason at position:' => '',
......
4272 4299
  'Total'                       => '',
4273 4300
  'Total Fees'                  => '',
4274 4301
  'Total Sales Orders Value'    => '',
4302
  'Total Stock'                 => '',
4275 4303
  'Total number of entries'     => '',
4276 4304
  'Total stock value'           => '',
4277 4305
  'Total sum'                   => '',
scripts/rose_auto_create_model.pl
83 83

  
84 84
    assembly                  => { parts_id => 'part', id => 'assembly_part' },
85 85
    assortment_items          => { parts_id => 'part' },
86
    purchase_basket_items     => { parts_id => 'part' },
87 86

  
88 87
    dunning                   => { trans_id => 'invoice', fee_interest_ar_id => 'fee_interest_invoice' },
89 88
  },
sql/Pg-upgrade2/add_orderer.sql
1
-- @tag: add_orderer
2
-- @description: Neue Spalte Besteller in purchase_basket_items, orderitems und delivery_order_items
3
-- @depends: purchase_basket_items
4
-- @ignore: 0
5

  
6
ALTER TABLE purchase_basket_items ADD COLUMN orderer_id INTEGER REFERENCES employee(id);
7
ALTER TABLE orderitems ADD COLUMN orderer_id INTEGER REFERENCES employee(id);
8
ALTER TABLE delivery_order_items ADD COLUMN orderer_id INTEGER REFERENCES employee(id);
sql/Pg-upgrade2/add_parts_order_qty.sql
1
-- @tag: add_parts_order_qty
2
-- @description: Bestellmenge für Artikel
3
-- @depends: purchase_basket_items
4
-- @ignore: 0
5

  
6
ALTER TABLE parts ADD COLUMN
7
  order_qty numeric(15, 5) NOT NULL DEFAULT 0;
sql/Pg-upgrade2/hmo_purchase_basket.sql
1
-- @tag: hmo_purchase_basket
2
-- @description: Änderungen HMO für Dispositionsmanager. Neue Spalte Besteller in orderitems und delivery_order_items
3
-- @depends: purchase_basket
4
-- @ignore: 0
5

  
6
ALTER TABLE purchase_basket_items DROP COLUMN description;
7
ALTER TABLE purchase_basket_items ADD COLUMN orderer_id INTEGER REFERENCES employee(id);
8
ALTER TABLE orderitems ADD COLUMN orderer_id INTEGER REFERENCES employee(id);
9
ALTER TABLE delivery_order_items ADD COLUMN orderer_id INTEGER REFERENCES employee(id);
sql/Pg-upgrade2/purchase_basket.sql
1
-- @tag: purchase_basket
2
-- @description: Tabelle für den Dispositionsmanager
3
-- @depends: release_3_5_1
4
-- @ignore: 0
5

  
6
CREATE TABLE purchase_basket_items (
7
  id SERIAL PRIMARY KEY,
8
  parts_id INTEGER REFERENCES parts(id),
9
  qty NUMERIC(15,5),
10
  description text,
11
  cleared BOOLEAN NOT NULL DEFAULT FALSE
12
);
sql/Pg-upgrade2/purchase_basket_2.sql
1
-- @tag: purchase_basket_2
2
-- @description: min_order_qty zu Tbl parts hinzufügen
3
-- @depends: purchase_basket
4
-- @ignore: 0
5

  
6
 ALTER TABLE parts ADD COLUMN min_order_qty numeric(15, 5);
sql/Pg-upgrade2/purchase_basket_items.sql
1
-- @tag: purchase_basket_items
2
-- @description: Tabelle für den Dispositionsmanager
3
-- @depends: release_3_8_0
4
-- @ignore: 0
5

  
6
CREATE TABLE purchase_basket_items (
7
  id SERIAL   PRIMARY KEY,
8
  part_id     INTEGER REFERENCES parts(id),
9
  qty         NUMERIC(15,5) NOT NULL,
10
  cleared     BOOLEAN NOT NULL            DEFAULT FALSE,
11
  itime       TIMESTAMP without time zone DEFAULT now(),
12
  mtime       TIMESTAMP without time zone
13
);
14
CREATE TRIGGER mtime_purchase_basket_items
15
  BEFORE UPDATE ON purchase_basket_items
16
  FOR EACH ROW EXECUTE PROCEDURE set_mtime();
t/controllers/disposition_manager/disposition_manager.t
29 29
  sellprice    => 5,
30 30
  lastcost     => 3,
31 31
  rop          => 20,
32
  ordersize    => 1,
32
  order_qty    => 1,
33 33
  warehouse_id => $wh->id,
34 34
  bin_id       => $bin->id,
35 35
  makemodels   => [ _create_makemodel_for_vendor(vendor => $vendor) ],
......
39 39
  partnumber   => 'TP 2',
40 40
  description  => 'Testpart 2 norop',
41 41
  rop          => 60,
42
  ordersize    => 2,
42
  order_qty    => 2,
43 43
)->save;
44 44
set_stock(part => $part2, bin_id => $bin->id, qty => 80);
45 45

  
......
48 48
    partnumber   => "TPO $i",
49 49
    description  => "Testpart onhand $i",
50 50
    rop          => 50,
51
    ordersize    => $i+2,
51
    order_qty    => $i+2,
52 52
    sellprice    => 5,
53 53
    lastcost     => 3,
54 54
    warehouse_id => $wh->id,
templates/webpages/disposition_manager/_show_vendor_parts.html
1
[%- USE HTML -%][%- USE LxERP -%][%- USE L -%][%- USE T8 -%][% USE P %]
2
[% USE Dumper %]
1
[%- USE HTML -%]
2
[%- USE LxERP -%]
3
[%- USE L -%]
4
[%- USE T8 -%]
5
[%- USE P -%]
6

  
3 7
<h2>[% 'All parts of vendor odered by onhand' | $T8 %]</h2>
4 8
<table width="100%">
5 9
    <thead>
6 10
     <tr class="listheading">
7
       <th>[% 'Purchase basket'                                 | $T8 %] </th>
8
       <th>[% 'Partnumber'                                      | $T8 %] </th>
9
       <th>[% 'Description'                                     | $T8 %] </th>
10
       <th>[% 'Onhand / Ordered'                                | $T8 %] </th>
11
       <th>[% 'Rop'                                             | $T8 %] </th>
12
       <th>[% 'Order quantity'                                  | $T8 %] </th>
13
       <th>[% 'Vendor'                                          | $T8 %] </th>
11
       <th>[% 'Purchase basket' | $T8 %] </th>
12
       <th>[% 'Partnumber'      | $T8 %] </th>
13
       <th>[% 'Description'     | $T8 %] </th>
14
       <th>[% 'Onhand'          | $T8 %] </th>
15
       <th>[% 'Ordered'         | $T8 %] </th>
16
       <th>[% 'Rop'             | $T8 %] </th>
17
       <th>[% 'Order quantity'  | $T8 %] </th>
18
       <th>[% 'Vendor'          | $T8 %] </th>
14 19
     </tr>
15 20
    </thead>
16 21
    <tbody>
17 22
    [% FOREACH makemodel_item = MAKEMODEL_ITEMS %]
18 23

  
19
[% # Dumper.dump_html('TEST') %]
20
[% # Dumper.dump_html(basket_item) %]
21 24
    [% SET select_size = basket_item.part.vendor_dropdown.size %]
22 25
      <tr class="listrow">
23
        <td>[% L.checkbox_tag('vendor_part_ids[+]', checked = '0',  value=makemodel_item.id) %]</td>
24
        <td>[% HTML.escape(makemodel_item.partnumber) %]
25
           [% IF makemodel_item.id %]
26
             <img class="odbuttons" onclick="kivi.DispositionManager.show_detail_dialog('[% makemodel_item.id %]','[% makemodel_item.partnumber %]')" src="image/icons/svg/information.svg"
27
                  title="[% 'Article details' | $T8 %]">
28
           [% END %]
26
        <td>[% L.checkbox_tag('vendor_part_ids[+]',
27
                 checked='0', value=makemodel_item.id) %]</td>
28
        <td>[% makemodel_item.presenter.part(target = '_blank') %]
29
          [% P.button_tag(
30
               "kivi.DispositionManager.show_detail_dialog("
31
                 _  makemodel_item.id _ "," _ makemodel_item.partnumber _
32
               ")",
33
               LxERP.t8('Details (one letter abbreviation)'),
34
               title=LxERP.t8('Article details'), class="button") %]
29 35
        </td>
30 36
        <td>[% HTML.escape(makemodel_item.description) %]</td>
31
        <td class="numeric">[% makemodel_item.onhand_as_number %] / [% makemodel_item.get_ordered_qty %]</td>
32
        <td class="numeric">[% makemodel_item.rop_as_number %]           </td>
33
        <td class="numeric">[% makemodel_item.qty_as_number %] </td>
34
        <td>[% L.select_tag('vendor_ids[]', makemodel_item.vendor_dropdown, value_key = 'value', title_key = 'title', default = makemodel_item.makemodels.item(0).make, size = select_size, style='width: 350px;' ) %]</td>
37
        <td class="numeric">[% makemodel_item.onhand_as_number %]</td>
38
        <td class="numeric">[% makemodel_item.get_ordered_qty %]</td>
39
        <td class="numeric">[% makemodel_item.rop_as_number %]</td>
40
        <td class="numeric">[% makemodel_item.qty_as_number %]</td>
41
        <td>[% L.select_tag('vendor_ids[]', makemodel_item.vendor_dropdown,
42
                 value_key='value', title_key='title',
43
                 default=makemodel_item.makemodels.item(0).make,
44
                 size=select_size, style='width:350px;' ) %]
45
        </td>
35 46
      </tr>
36 47
    [% END %]
37 48
    </tbody>
templates/webpages/disposition_manager/list_parts.html
1
[%- USE HTML -%][%- USE LxERP -%][%- USE L -%][%- USE T8 -%]
2
[% USE Dumper %]
1
[%- USE HTML -%]
2
[%- USE LxERP -%]
3
[%- USE L -%]
4
[%- USE T8 -%]
5

  
3 6
[%- INCLUDE 'common/flash.html' %]
4 7
<hr>
5 8
<h2>[% 'Short onhand' | $T8 %]</h2>
......
8 11
  <table width="100%">
9 12
    <thead>
10 13
     <tr class="listheading">
11
       <th>[% L.checkbox_tag('check_all', checkall="[data-checkall=1]") %][% 'Purchase basket' | $T8 %] </th>
14
       <th>
15
         [% L.checkbox_tag('check_all', checkall='[data-checkall=1]') %]
16
         [% 'Purchase basket' | $T8 %]
17
       </th>
12 18
       <th>[% 'Partnumber'                                      | $T8 %] </th>
13 19
       <th>[% 'Description'                                     | $T8 %] </th>
14 20
       <th>[% 'Vendor'                                          | $T8 %] </th>
......
20 26
    [% FOREACH part = PARTS %]
21 27
      [% IF !part.get_open_ordered_qty %]
22 28
      <tr class="listrow">
23
        <td>[% IF part.makemodels.size %][% L.checkbox_tag('ids[+]', "data-checkall"=1, checked = '1', value=part.id) %][% ELSE %][% 'No Vendor' | $T8 %][% END %]</td>
29
        <td>
30
          [% IF part.makemodels.size %]
31
            [% L.checkbox_tag('ids[+]', 'data-checkall'=1,
32
                 checked = '1', value=part.id) %]
33
          [% ELSE %]
34
            [% 'No Vendor' | $T8 %]
35
          [% END %]
36
        </td>
24 37
        <td>[% HTML.escape(part.partnumber) %]</td>
25 38
        <td>[% HTML.escape(part.description) %]</td>
26
        <td>[% L.select_tag('vendor_ids[]', part.vendor_dropdown, value_key = 'value', title_key = 'title', default = basket_item.part.makemodels.item(0).make, size = select_size, style='width: 250px;' ) %]</td>
27
        <td class="numeric">[% part.onhandqty_as_number %]</td>
28
        <td class="numeric">[% part.rop_as_number %]           </td>
29
        <td class="numeric">[% part.ordersize_as_number %] </td>
39
        <td>
40
          [% L.select_tag(
41
               'vendor_ids[]', part.vendor_dropdown,
42
               value_key = 'value', title_key = 'title',
43
               default = basket_item.part.makemodels.item(0).make,
44
               size = select_size, style='width: 250px;' ) %]
45
        </td>
46
        <td class="numeric">[% LxERP.format_amount(part.onhandqty, 2) %]</td>
47
        <td class="numeric">[% part.rop_as_number %]</td>
48
        <td class="numeric">[% part.order_qty_as_number %]</td>
30 49
      </tr>
31 50
      [% END %]
32 51
    [% END %]
33 52
  </table>
34 53
</form>
35 54
<hr>
36
<h2>[% 'Short onhand Ordered' | $T8 %]</h2>
37
<<<<<<< HEAD
38
<div style="margin:1em;">
39
<table width="100%">
40
  <thead>
41
   <tr class="listheading">
42
     <th>[% 'Partnumber'       | $T8 %] </th>
43
     <th>[% 'Description'      | $T8 %] </th>
44
     <th>[% 'onhand'           | $T8 %] </th>
45
     <th>[% 'rop'              | $T8 %] </th>
46
     <th>[% 'Ordered purchase' | $T8 %] </th>
47
   </tr>
48
  </thead>
49
  [% FOREACH part = PARTS %]
50
    [% IF part.get_open_ordered_qty %]
51
    <tr class="listrow">
52
      <td>[% HTML.escape(part.partnumber) %]</td>
53
      <td>[% HTML.escape(part.description) %] </td>
54
      <td class="numeric">[% part.onhand_as_number %]         </td>
55
      <td class="numeric">[% part.rop_as_number %]            </td>
56
      <td class="numeric">[% part.get_open_ordered_qty_as_number %] </td>
57
    </tr>
58
    [% END %]
59
  [% END %]
60
</table>
61
=======
62
>>>>>>> a1d2278510 (Dispomanager Meldebestand Bestellte Artikel als Report)
63 55
</div>
templates/webpages/disposition_manager/reorder_level_list/report_bottom.html
1
[% USE L %]
2
[%- L.paginate_controls(models=SELF.models)  %]
templates/webpages/disposition_manager/show_purchase_basket.html
1
[%- USE HTML -%][%- USE LxERP -%][%- USE L -%][%- USE T8 -%][% USE P %]
2
[% USE Dumper %]
1
[%- USE HTML -%]
2
[%- USE LxERP -%]
3
[%- USE L -%]
4
[%- USE T8 -%]
5
[%- USE P -%]
6

  
3 7
[%- INCLUDE 'common/flash.html' %]
4 8
<h1>[% title %]</h1>
5 9
[% # Dumper.dump_html(BASKET_ITEMS) %]
6 10
<form id="purchasebasket" style="margin:1em;">
7 11
  <div>
8
      <p>[% 'Vendor' | $T8 %]: [% P.customer_vendor.picker('vendor_id2', FORM.vendor_id2, type='vendor') %]</p>
9
      <p>[% 'Partsgroup' | $T8 %]: [% P.partsgroup.picker('partsgroup_id', FORM.partsgroup_id, with_empty=1, onchange=> "kivi.DispositionManager.filter_partsgroup();") %]</p>
12
    <p>
13
      [% 'Vendor' | $T8 %]:
14
      [% P.customer_vendor.picker('vendor_id2', FORM.vendor_id2, type='vendor') %]
15
    </p>
10 16
  </div>
11 17
<div>
12 18
  <table id="baskettable" width="100%">
13 19
    <thead>
14 20
     <tr class="listheading">
15
       <th>[% L.checkbox_tag("", id="check_all", checkall="[data-checkall=1]") %][% 'Purchase basket' | $T8 %] </th>
21
       <th>
22
         [% L.checkbox_tag("", id="check_all", checkall="[data-checkall=1]") %]
23
         [% 'Purchase basket' | $T8 %]
24
       </th>
16 25
       <th>[% 'Partnumber'                                      | $T8 %] </th>
17 26
       <th>[% 'Description'                                     | $T8 %] </th>
... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet.

Auch abrufbar als: Unified diff