Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 8fa9177a

Von Tamino Steinert vor mehr als 1 Jahr hinzugefügt

  • ID 8fa9177a4cfa72faffa3c535310fb5c60bfd0b39
  • Vorgänger a7d84310
  • Nachfolger 2401956f

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
266 266

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

  
......
1588 1588
sub _is_in_purchase_basket {
1589 1589
  my ( $self ) = @_;
1590 1590

  
1591
  return SL::DB::Manager::PurchaseBasketItem->get_all_count( query => [ parts_id => $self->part->id ] );
1591
  return SL::DB::Manager::PurchaseBasketItem->get_all_count( query => [ part_id => $self->part->id ] );
1592 1592
}
1593 1593
sub _is_ordered {
1594 1594
  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;
......
103 104
__PACKAGE__->before_save('_before_save_set_partnumber');
104 105
__PACKAGE__->before_save('_before_save_set_assembly_weight');
105 106

  
106
sub onhandqty_as_number {
107
  my ($self, $string) = @_;
108
  $self->onhandqty($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
109
  return $::form->format_amount(\%::myconfig, $self->onhandqty, 2);
110
}
111

  
112 107
sub init_onhandqty{
113 108
  my ($self) = @_;
114 109
  my $qty = SL::Helper::Inventory::get_onhand(part => $self->id);
115 110
  return $qty;
116 111
}
117 112

  
118
sub stockqty_as_number {
119
  my ($self, $string) = @_;
120
  $self->stockqty($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
121
  return $::form->format_amount(\%::myconfig, $self->stockqty, 2);
122
}
123

  
124 113
sub init_stockqty{
125 114
  my ($self) = @_;
126 115
  my $qty = SL::Helper::Inventory::get_stock(part => $self->id);
127 116
  return $qty;
128 117
}
129 118

  
130
sub get_open_ordered_qty_as_number {
131
  my ($self, $string) = @_;
132
  $self->get_open_ordered_qty($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
133
  return $::form->format_amount(\%::myconfig, $self->get_open_ordered_qty, 2);
134
}
135

  
136 119
sub init_get_open_ordered_qty {
137 120
  my $self   = shift;
138 121
  my $result = SL::DB::Manager::Part->get_open_ordered_qty($self->id);
139 122

  
140 123
  return $result;
141 124
}
142
#sub reservedqty_as_number {
143
#  my ($self, $string) = @_;
144
#  $self->reservedqty($::form->parse_amount(\%::myconfig, $string)) if @_ > 1;
145
#  return $::form->format_amount(\%::myconfig, $self->reservedqty, $self->places);
146
#}
147

  
148
#sub init_onhandqty{
149
#  my ($self) = @_;
150
#  my $qty = get_onhand(part => $self->id);
151
#  return $qty;
152
#}
153
>>>>>>> cf3ff322d1 (Einkaufshelfer/Dispositionsmanager bescheleunigt)
154 125

  
155 126
sub _before_save_set_partnumber {
156 127
  my ($self) = @_;
......
642 613
}
643 614

  
644 615
sub vendor_dropdown {
645
  my ( $self ) = @_;
616
  my ($self) = @_;
646 617

  
647 618
  my @vendor_dd;
648
  # $main::lxdebug->dump(0, 'WH:MakeModels ', $_[0]->makemodels);
649 619

  
650
  foreach my $mm ( @{$_[0]->makemodels} ){
620
  foreach my $mm ( @{$self->makemodels} ){
651 621
    my $vendor = SL::DB::Manager::Vendor->get_first( where => [ id => $mm->make ] );
652 622
    my @tmp = ({ title => $vendor->vendornumber . "--" .$vendor->name . " (" . $::form->format_amount(\%::myconfig, $mm->lastcost, 2) . ")", value => $vendor->{id} });
653 623
    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
308 308
  'All general ledger entries'  => 'Alle Hauptbucheinträge',
309 309
  'All groups'                  => 'Alle Gruppen',
310 310
  'All modules'                 => 'Alle Module',
311
  'All parts of vendor odered by onhand' => 'Alle Artikel des Lieferanten sortiert nach Lagerbestand',
311 312
  'All partsgroups'             => 'Alle Warengruppen',
312 313
  'All pay postings successfully imported.' => 'Alle Lohnbuchungen erfolgreich importiert.',
313 314
  'All payments have already been posted.' => 'Es wurden bereits alle Zahlungen verbucht.',
......
331 332
  'Already counted'             => 'Bereits erfasst',
332 333
  'Already imported entries (duplicates)' => 'Bereits importierte Einträge (Duplikate)',
333 334
  'Already imported: '          => 'Bereits importiert:',
334
  'Also with reserved Qty'      => 'inklusive Reservierungen',
335 335
  '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.',
336 336
  '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.',
337 337
  'Always save orders with a projectnumber (create new projects)' => 'Aufträge immer mit Projektnummer speichern (neue Projekte erstellen)',
......
386 386
  'Article Code'                => 'Artikelkürzel',
387 387
  'Article classification'      => 'Artikel-Klassifizierung',
388 388
  'Article data'                => 'Artikeldaten',
389
  'Article details'             => 'Artikeldetails',
389 390
  'Article type'                => 'Artikeltyp',
390 391
  'Articles'                    => 'Artikel',
391 392
  '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.',
......
446 447
  'Automatically created invoice for fee and interest for dunning %s' => 'Automatisch erzeugte Rechnung für Gebühren und Zinsen zu Mahnung %s',
447 448
  'Available'                   => 'Verfügbar',
448 449
  'Available Prices'            => 'Mögliche Preise',
450
  'Available Stock'             => 'verfügbarer Bestand',
449 451
  'Available qty'               => 'Lagerbestand',
450 452
  'Available to all users'      => 'Für alle BenutzerInnen verfügbar',
451 453
  'BALANCE SHEET'               => 'BILANZ',
......
1200 1202
  'Detailed information about this customer' => 'Detaillierte Informationen über diesen Kunden',
1201 1203
  'Details'                     => 'Details',
1202 1204
  'Details (one letter abbreviation)' => 'D',
1205
  'Details of article number "#1"' => 'Details von Artikel "#1"',
1203 1206
  'Details: #1'                 => 'Details: #1',
1204 1207
  'Developer Tools'             => 'Developer Tools',
1205 1208
  'Dial command missing in kivitendo configuration\'s [cti] section' => 'Wählbefehl fehlt im Abschnitt [cti] der kivitendo-Konfiguration',
......
1220 1223
  'Display in basic data tab'   => 'Im Reiter Basisdaten anzeigen',
1221 1224
  'Display options'             => 'Anzeigeoptionen',
1222 1225
  'Displayable Name Preferences' => 'Einstellungen für Anzeigenamen',
1223
  'Disposition manager'         => 'Dispositionsmanager',
1224 1226
  'Do not change the tax rate of taxkey 0.' => 'Ändern Sie nicht den Steuersatz vom Steuerschlüssel 0.',
1225 1227
  'Do not check for duplicates' => 'Nicht nach Dubletten suchen',
1226 1228
  'Do not create Factur-X/ZUGFeRD invoices' => 'Keine Factur-X-/ZUGFeRD-Rechnungen erzeugen',
......
2603 2605
  'Order Date missing!'         => 'Auftragsdatum fehlt!',
2604 2606
  'Order Number'                => 'Auftragsnummer',
2605 2607
  'Order Number missing!'       => 'Auftragsnummer fehlt!',
2608
  'Order Size'                  => 'Bestellmenge',
2606 2609
  'Order amount'                => 'Auftragswert',
2607 2610
  'Order data'                  => 'Auftragsinformationen',
2608 2611
  'Order deleted!'              => 'Auftrag gelöscht!',
......
2614 2617
  'Order/Item row name'         => 'Name der Auftrag-/Positions-Zeilen',
2615 2618
  'Order/Item/Stock row name'   => 'Name der Auftrag-/Positions-/Lager-Zeilen',
2616 2619
  'Order/RFQ Number'            => 'Belegnummer',
2617
  'Order:'                      => 'Auftrag:',
2618 2620
  'OrderItem'                   => 'Position',
2619 2621
  'Ordered'                     => 'Von Kunden bestellt',
2620
  'Ordered qty'                 => 'bestellt',
2621 2622
  'Ordered purchase'            => 'Bestellte Menge',
2622 2623
  'Orderer'                     => 'BestellerIn',
2623 2624
  'Orders'                      => 'Aufträge',
......
2690 2691
  'Part Type'                   => 'Artikel-Typ',
2691 2692
  'Part Unit'                   => 'Einheit',
2692 2693
  'Part added to purchasebasket' => 'Zu Einkaufkorb hinzugefügt',
2693
  'Part already in purchasebasket or has no vendor' => 'Artikel ist schon im Bestellwarenkorb oder hat keinen Lieferanten',
2694 2694
  'Part already in purchasebasket' => 'Schon im Einkaufskorb',
2695
  'Part already in purchasebasket or has no vendor' => 'Artikel ist schon im Bestellwarenkorb oder hat keinen Lieferanten',
2695 2696
  'Part already ordered'        => 'Artikel ist bestellt',
2696 2697
  'Part classifications'        => 'Artikel-Klassifizierungen',
2697 2698
  'Part marked as "Shop part"'  => 'Markiert als Shopartikel',
......
2707 2708
  'Parts Classification'        => 'Artikel-Klassifizierung',
2708 2709
  'Parts Inventory'             => 'Warenliste',
2709 2710
  'Parts Master Data'           => 'Artikelstammdaten',
2710
  'Parts short onhand'          => 'Artikel mit geringer Lagermenge',
2711 2711
  'Parts with existing part numbers' => 'Artikel mit existierender Artikelnummer',
2712 2712
  'Parts, services and assemblies' => 'Waren, Dienstleistungen und Erzeugnisse',
2713 2713
  'Partsgroup'                  => 'Warengruppe',
......
3139 3139
  'Rename Images'               => 'Bilder umbenennen',
3140 3140
  'Rename attachment'           => 'Dateianhang umbenennen',
3141 3141
  'Renumber sections and function blocks' => 'Abschnitte/Funktionsblöcke neu nummerieren',
3142
  'Reorder Level List'          => 'Meldebestandsliste',
3142 3143
  'Replace legacy order controller with new one' => 'Neuen Auftrags-Controller verwenden',
3143 3144
  '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.',
3144 3145
  'Report Positions'            => 'Berichte',
......
3484 3485
  'Shoporders'                  => 'Shopbest.',
3485 3486
  'Shops'                       => 'Webshops',
3486 3487
  'Short'                       => 'Knapp',
3487
  'Short onhand Ordered'        => 'Geringe Lagermenge, aber bestellt',
3488 3488
  'Short onhand'                => 'Geringe Lagermenge',
3489 3489
  '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.',
3490 3490
  '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.',
......
4161 4161
  'There are no entries that match the filter.' => 'Es gibt keine Einträge, auf die der Filter zutrifft.',
4162 4162
  'There are no items in stock.' => 'Dieser Artikel ist nicht eingelagert.',
4163 4163
  'There are no items on your TODO list at the moment.' => 'Ihre Aufgabenliste enthält momentan keine Einträge.',
4164
  'There are no items selected' => 'Es wurden keine Positionen ausgewählt',
4164 4165
  'There are no orders for the selected time period.' => 'Es gibt für den ausgewählten Zeitraum keine Aufträge.',
4165 4166
  'There are no record templates yet.' => 'Es gibt noch keine Belegvorlagen.',
4166 4167
  'There are parts with no reclamation reason at position:' => 'Es git Artikel mit keinem Reklamationsgrund an Position:',
......
4322 4323
  'Total'                       => 'Summe',
4323 4324
  'Total Fees'                  => 'Kumulierte Gebühren',
4324 4325
  'Total Sales Orders Value'    => 'Auftragseingang',
4326
  'Total Stock'                 => 'Gesamtbestand',
4325 4327
  'Total number of entries'     => 'Gesamtzahl Einträge',
4326 4328
  'Total stock value'           => 'Gesamter Bestandswert',
4327 4329
  'Total sum'                   => 'Gesamtsumme',
......
4536 4538
  'VAT ID'                      => 'USt-IdNr.',
4537 4539
  'VAT ID and/or taxnumber must be given.' => 'UStId und/oder Steuernummer muss angegeben werden.',
4538 4540
  'VN'                          => 'Kred.-Nr.',
4539
  'VPE'                         => '',
4540 4541
  'Valid'                       => 'Gültig',
4541 4542
  '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.',
4542 4543
  'Valid for Purchase'          => 'Gültig für Einkauf',
......
4939 4940
  'once'                        => 'einmalig',
4940 4941
  'one time'                    => 'einmalig',
4941 4942
  'one-time execution'          => 'einmalige Ausführung',
4942
  'onhand'                      => 'Auf Lager',
4943 4943
  'only OB Transactions'        => 'nur EB-Buchungen',
4944 4944
  'open'                        => 'Offen',
4945 4945
  'order'                       => 'Reihenfolge',
......
4983 4983
  'revert deleted'              => 'löschen rückgängig',
4984 4984
  'rfq_list'                    => 'anfragenliste',
4985 4985
  'rma_delivery_order_list'     => 'lieferscheinliste_rma',
4986
  'rop'                         => 'Mindestlagerbestand',
4987 4986
  'running'                     => 'läuft',
4988 4987
  'sales tax identification number' => 'USt-IdNr.',
4989 4988
  'sales_delivery_order_list'   => 'lieferscheinliste_verkauf',
locale/en/all
274 274
  'Add text block'              => '',
275 275
  'Add time recording article'  => '',
276 276
  'Add title'                   => '',
277
  'Add to basket'               => '',
278
  'Add to purchase basket'      => '',
277 279
  'Add unit'                    => '',
278 280
  'Added sections and function blocks: #1' => '',
279 281
  'Added text blocks: #1'       => '',
......
306 308
  'All general ledger entries'  => '',
307 309
  'All groups'                  => '',
308 310
  'All modules'                 => '',
311
  'All parts of vendor odered by onhand' => '',
309 312
  'All partsgroups'             => '',
310 313
  'All pay postings successfully imported.' => '',
311 314
  'All payments have already been posted.' => '',
......
383 386
  'Article Code'                => '',
384 387
  'Article classification'      => '',
385 388
  'Article data'                => '',
389
  'Article details'             => '',
386 390
  'Article type'                => '',
387 391
  'Articles'                    => '',
388 392
  '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' => '',
......
443 447
  'Automatically created invoice for fee and interest for dunning %s' => '',
444 448
  'Available'                   => '',
445 449
  'Available Prices'            => '',
450
  'Available Stock'             => '',
446 451
  'Available qty'               => '',
447 452
  'Available to all users'      => '',
448 453
  'BALANCE SHEET'               => '',
......
894 899
  'Create new version'          => '',
895 900
  'Create one from the context menu by right-clicking on this text.' => '',
896 901
  'Create order'                => '',
902
  'Create purchase order'       => '',
897 903
  'Create sales invoices with Factur-X/ZUGFeRD data' => '',
898 904
  'Create sales invoices with Swiss QR-bill' => '',
899 905
  'Create tables'               => '',
......
1195 1201
  'Detailed information about this contact' => '',
1196 1202
  'Detailed information about this customer' => '',
1197 1203
  'Details'                     => '',
1198
  'Details (one letter abbreviation)' => '',
1204
  'Details (one letter abbreviation)' => 'D',
1205
  'Details of article number "#1"' => '',
1199 1206
  'Details: #1'                 => '',
1200 1207
  'Developer Tools'             => '',
1201 1208
  'Dial command missing in kivitendo configuration\'s [cti] section' => '',
......
2189 2196
  'List of jobs'                => '',
2190 2197
  'List of tax zones'           => '',
2191 2198
  'List open SEPA exports'      => '',
2199
  'List short onhand'           => '',
2192 2200
  'List time recordings of all staff members' => '',
2193 2201
  'Listprice'                   => '',
2194 2202
  'Load'                        => '',
......
2290 2298
  'Microfiche'                  => '',
2291 2299
  'Minimize Panel'              => '',
2292 2300
  'Minimum Amount'              => '',
2301
  'Minimum order quantity'      => '',
2293 2302
  'Miscellaneous'               => '',
2294 2303
  'Missing \'description\' field.' => '',
2295 2304
  'Missing \'tag\' field.'      => '',
......
2390 2399
  'No Shopdescription'          => '',
2391 2400
  'No Shopimages'               => '',
2392 2401
  'No VAT Info for this Factur-X/ZUGFeRD invoice, please ask your vendor to add this for his Factur-X/ZUGFeRD data.' => '',
2402
  'No Vendor'                   => '',
2393 2403
  'No Vendor was found matching the search parameters.' => '',
2394 2404
  'No account selected. Please select an account.' => '',
2395 2405
  'No action defined.'          => '',
......
2479 2489
  'No valid number entered for pricegroup "#1".' => '',
2480 2490
  'No vendor has been selected yet.' => '',
2481 2491
  'No vendor selected or found!' => '',
2492
  'No vendors to add to purchasebasket' => '',
2482 2493
  'No warehouse has been created yet or the quantity of the bins is not configured yet.' => '',
2483 2494
  'No webdav backend enabled.'  => '',
2484 2495
  'No year given for method year' => '',
......
2593 2604
  'Order Date missing!'         => '',
2594 2605
  'Order Number'                => '',
2595 2606
  'Order Number missing!'       => '',
2607
  'Order Size'                  => '',
2596 2608
  'Order amount'                => '',
2597 2609
  'Order data'                  => '',
2598 2610
  'Order deleted!'              => '',
2599 2611
  'Order item search'           => '',
2600 2612
  'Order probability'           => '',
2601 2613
  'Order probability & expected billing date' => '',
2614
  'Order quantity'              => '',
2602 2615
  'Order value periodicity'     => '',
2603 2616
  'Order/Item row name'         => '',
2604 2617
  'Order/Item/Stock row name'   => '',
2605 2618
  'Order/RFQ Number'            => '',
2606 2619
  'OrderItem'                   => '',
2607 2620
  'Ordered'                     => '',
2621
  'Ordered purchase'            => '',
2622
  'Orderer'                     => '',
2608 2623
  'Orders'                      => '',
2609 2624
  'Orders / Delivery Orders deleteable' => '',
2610 2625
  'Orders in Webshop'           => '',
......
2674 2689
  'Part Test'                   => '',
2675 2690
  'Part Type'                   => '',
2676 2691
  'Part Unit'                   => '',
2692
  'Part added to purchasebasket' => '',
2693
  'Part already in purchasebasket' => '',
2694
  'Part already in purchasebasket or has no vendor' => '',
2695
  'Part already ordered'        => '',
2677 2696
  'Part classifications'        => '',
2678 2697
  'Part marked as "Shop part"'  => '',
2679 2698
  'Part picker'                 => '',
......
2973 2992
  'Purchase Reclamation'        => '',
2974 2993
  'Purchase Reclamations'       => '',
2975 2994
  'Purchase Reclamations deleteable' => '',
2995
  'Purchase basket'             => '',
2976 2996
  'Purchase delivery order'     => '',
2977 2997
  'Purchase invoices'           => '',
2978 2998
  'Purchase invoices changeable' => '',
......
3089 3109
  'Registration'                => '',
3090 3110
  'Relaxed (UTF-8)'             => '',
3091 3111
  'Release From Stock'          => '',
3112
  'Reload'                      => '',
3092 3113
  'Remaining'                   => '',
3093 3114
  'Remaining Amount'            => '',
3094 3115
  'Remaining Net Amount'        => '',
......
3117 3138
  'Rename Images'               => '',
3118 3139
  'Rename attachment'           => '',
3119 3140
  'Renumber sections and function blocks' => '',
3141
  'Reorder Level List'          => '',
3120 3142
  'Replace legacy order controller with new one' => '',
3121 3143
  '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.' => '',
3122 3144
  'Report Positions'            => '',
......
3182 3204
  'Risk'                        => '',
3183 3205
  'Risk levels'                 => '',
3184 3206
  'Risks'                       => '',
3207
  'Rop'                         => '',
3185 3208
  'Rounding'                    => '',
3186 3209
  'Rounding Gain'               => '',
3187 3210
  'Rounding Loss'               => '',
......
3461 3484
  'Shoporders'                  => '',
3462 3485
  'Shops'                       => '',
3463 3486
  'Short'                       => '',
3487
  'Short onhand'                => '',
3464 3488
  '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.' => '',
3465 3489
  '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.' => '',
3466 3490
  'Should ap transactions be and when should they be changeable or deleteable after posting?' => '',
......
3522 3546
  'Show overdue sales quotations and requests for quotations...' => '',
3523 3547
  'Show parts'                  => '',
3524 3548
  'Show parts longdescription (notes) in select list' => '',
3549
  'Show purchase basket'        => '',
3525 3550
  'Show purchase letters report' => '',
3526 3551
  'Show record tab in customer' => '',
3527 3552
  'Show record tab in vendor'   => '',
......
4134 4159
  'There are no entries that match the filter.' => '',
4135 4160
  'There are no items in stock.' => '',
4136 4161
  'There are no items on your TODO list at the moment.' => '',
4162
  'There are no items selected' => '',
4137 4163
  'There are no orders for the selected time period.' => '',
4138 4164
  'There are no record templates yet.' => '',
4139 4165
  'There are parts with no reclamation reason at position:' => '',
......
4295 4321
  'Total'                       => '',
4296 4322
  'Total Fees'                  => '',
4297 4323
  'Total Sales Orders Value'    => '',
4324
  'Total Stock'                 => '',
4298 4325
  'Total number of entries'     => '',
4299 4326
  'Total stock value'           => '',
4300 4327
  '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>
18 27
       <th>[% 'Onhand'                                          | $T8 %] </th>
......
24 33
    <tbody>
25 34
    [% FOREACH basket_item = BASKET_ITEMS %]
26 35

  
27
[% # Dumper.dump_html('TEST') %]
28
[% # Dumper.dump_html(basket_item) %]
29 36
    [% SET select_size = basket_item.part.vendor_dropdown.size %]
... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet.

Auch abrufbar als: Unified diff