Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 756f5042

Von Bernd Bleßmann vor 24 Tagen hinzugefügt

  • ID 756f5042b90a76a05f6409df4e5da764287e7722
  • Vorgänger a88f2c21
  • Nachfolger 34f13160

Zwischeninventur-Abgleich: Bewegungen zwischen Zählung und Abgleich berücksichtigen

Unterschiede anzeigen:

SL/Controller/StockCountingReconciliation.pm
4 4
use parent qw(SL::Controller::Base);
5 5

  
6 6
use English qw(-no_match_vars);
7
use List::Util qw(sum0);
7
use List::Util qw(sum sum0);
8 8
use POSIX qw(strftime);
9 9

  
10 10
use SL::Controller::Helper::GetModels;
11 11
use SL::Controller::Helper::ReportGenerator;
12 12
use SL::DB::Employee;
13
use SL::DB::Inventory;
13 14
use SL::DB::StockCounting;
14 15
use SL::DB::StockCountingItem;
15 16
use SL::Helper::Flash qw(flash_later);
......
18 19
use SL::ReportGenerator;
19 20
use SL::WH;
20 21

  
22

  
23
            use Data::Dumper;
24

  
21 25
use Rose::Object::MakeMethods::Generic(
22 26
  #scalar => [ qw() ],
23 27
  'scalar --get_set_init' => [ qw(countings models) ],
......
74 78
  $objects = \@grouped_objects;
75 79

  
76 80
  $self->get_stocked($objects);
81
  $self->get_inbetweens($objects);
77 82

  
78 83
  $self->setup_list_action_bar;
79 84
  $self->report_generator_list_objects(report => $self->{report}, objects => $objects);
......
82 87
sub action_reconcile {
83 88
  my ($self) = @_;
84 89

  
85
  my $counting_id    = $::form->{filter}->{counting_id};
86
  my $counting_items = SL::DB::Manager::StockCountingItem->get_all(query => [counting_id => $counting_id]);
87

  
88
  my $counted_qty    = sum0 map { $_->qty } @$counting_items;
89
  my $stocked_qty    = $counting_items->[0]->part->get_stock(bin_id => $counting_items->[0]->bin_id);
90

  
91
  my $comment        = t8('correction from stock counting (counting "#1")', $counting_items->[0]->counting->name);
90
  my $counting = SL::DB::StockCounting->new(id => $::form->{counting_id})->load;
91
  # todo: sanity checks
92
  # return if $counting->is_reconciliated;
93
  # return if scalar(@{$counting->items}) == 0;
94

  
95
  my $counting_items = $counting->items;
96

  
97
  $self->get_stocked($counting_items);
98
  $self->get_inbetweens($counting_items);
99

  
100
  my $counted_by_part_and_bin;
101
  my $stocked_by_part_and_bin;
102
  my $inbetweens_by_part_and_bin;
103
  my $item_ids_by_part_and_bin;
104
  foreach my $item (@$counting_items) {
105
    $counted_by_part_and_bin   ->{$item->part_id}->{$item->bin_id}  += $item->qty;
106
    $stocked_by_part_and_bin   ->{$item->part_id}->{$item->bin_id} ||= $item->{stocked};
107
    $inbetweens_by_part_and_bin->{$item->part_id}->{$item->bin_id} ||= $item->{inbetweens};
108
    push @{$item_ids_by_part_and_bin->{$item->part_id}->{$item->bin_id}}, $item->id;
109
  }
92 110

  
93
  my $transfer_qty   = $counted_qty - $stocked_qty;
94
  my $src_or_dst     = $transfer_qty < 0? 'src' : 'dst';
95
  $transfer_qty      = abs($transfer_qty);
111
  my $comment = t8('correction from stock counting (counting "#1")', $counting->name);
96 112

  
97 113
  my $transfer_error;
98
  # do stock
99 114
  $::form->throw_on_error(sub {
100 115
    eval {
101
      WH->transfer({
102
        parts                   => $counting_items->[0]->part,
103
        $src_or_dst.'_bin'      => $counting_items->[0]->bin,
104
        $src_or_dst.'_wh'       => $counting_items->[0]->bin->warehouse,
105
        qty                     => $transfer_qty,
106
        unit                    => $counting_items->[0]->part->unit,
107
        transfer_type           => 'correction',
108
        comment                 => $comment,
109
      });
116
      SL::DB->client->with_transaction(sub {
117
        foreach my $part_id (keys %$counted_by_part_and_bin) {
118
          foreach my $bin_id (keys %{$counted_by_part_and_bin->{$part_id}}) {
119
            my $counted_qty   = $counted_by_part_and_bin   ->{$part_id}->{$bin_id};
120
            my $stocked_qty   = $stocked_by_part_and_bin   ->{$part_id}->{$bin_id};
121
            my $inbetween_qty = $inbetweens_by_part_and_bin->{$part_id}->{$bin_id};
122

  
123
            my $transfer_qty  = $counted_qty - $stocked_qty + $inbetween_qty;
124

  
125
            my $src_or_dst = $transfer_qty < 0? 'src' : 'dst';
126
            $transfer_qty  = abs($transfer_qty);
127

  
128
            # Do stock.
129
            # todo: run in transaction and record the inventory id in the counting items
130
            my %transfer_params = (
131
              parts_id              => $part_id,
132
              $src_or_dst.'_bin_id' => $bin_id,
133
              qty                   => $transfer_qty,
134
              transfer_type         => 'correction',
135
              comment               => $comment,
136
            );
137

  
138
            my @trans_ids = WH->transfer(\%transfer_params);
139

  
140
            if (scalar(@trans_ids) != 1) {
141
              die "Program logic error: no error, but no transfer" if scalar(@trans_ids) == 0;
142
              die "Program logic error: too many transfers"        if scalar(@trans_ids) >  1;
143
            }
144

  
145
            # Get inventory entries via trans_ids-
146
            my $inventories = SL::DB::Manager::Inventory->get_all(where => [trans_id => $trans_ids[0]]);
147
            if (scalar(@$inventories) != 1) {
148
              die "Program logic error: no error, but no inventory entry" if scalar(@$inventories) == 0;
149
              die "Program logic error: too many inventory entries"       if scalar(@$inventories) >  1;
150
            }
151

  
152

  
153
            SL::DB::Manager::StockCountingItem->update_all(set   => {correction_inventory_id => $inventories->[0]->id},
154
                                                           where => [id => $item_ids_by_part_and_bin->{$part_id}->{$bin_id}]);
155
          }
156
        }
157

  
158
        1;
159
      }) or do { die SL::DB->client->error; }; # end of with_transaction
160

  
110 161
      1;
111
    } or do { $transfer_error = ref($EVAL_ERROR) eq 'SL::X::FormError' ? $EVAL_ERROR->error : $EVAL_ERROR; }
112
  });
162
    } or do { $transfer_error = ref($EVAL_ERROR) eq 'SL::X::FormError' ? $EVAL_ERROR->error : $EVAL_ERROR; }; # end of eval
163

  
164
  });                           # end of throw_on_error
113 165

  
114 166
  if ($transfer_error) {
115 167
    flash_later('error', $transfer_error);
......
146 198
  my $report      = SL::ReportGenerator->new(\%::myconfig, $::form);
147 199
  $self->{report} = $report;
148 200

  
149
  my @columns = qw(ids counting part bin qty stocked);
201
  my @columns = qw(counting part bin qty stocked inbetweens);
150 202

  
151 203
  my %column_defs = (
152 204
    counting   => { text => t8('Stock Counting'), sub => sub { $_[0]->counting->name }, },
......
156 208
    bin        => { text => t8('Bin'),            sub => sub { $_[0]->bin->full_description } },
157 209
    employee   => { text => t8('Employee'),       sub => sub { $_[0]->employee ? $_[0]->employee->safe_name : '---'} },
158 210
    stocked    => { text => t8('Stocked Qty'),    sub => sub { _format_total($_[0]->{stocked}) }, align => 'right'},
211
    inbetweens => { text => t8('Inbetweens Qty'), sub => sub { _format_total($_[0]->{inbetweens}) }, align => 'right'},
159 212
  );
160 213

  
161 214
  # remove columns from defs which are not in @columns
......
187 240

  
188 241
  $report->set_options(
189 242
    raw_top_info_text    => $self->render('stock_counting_reconciliation/report_top',    { output => 0 }),
190
    raw_bottom_info_text => $self->render('stock_counting_reconciliation/report_bottom', { output => 0 }, models => $self->models),
243
    raw_bottom_info_text => $self->render('stock_counting_reconciliation/report_bottom', { output => 0 }, models => $self->models, counting_id => $::form->{filter}->{counting_id}),
191 244
    attachment_basename  => t8('stock_countings') . strftime('_%Y%m%d', localtime time),
192 245
  );
193 246
}
......
218 271
  $_->{stocked} = $_->part->get_stock(bin_id => $_->bin_id) for @$objects;
219 272
}
220 273

  
274
sub get_inbetweens {
275
  my ($self, $objects) = @_;
276

  
277
  # Get changes in stock while a counting was active.
278
  # (i.e. from start of counting till now).
279
  # Use itime, because shippingdate has no time component.
280
  # Ignore stock counting corrections.
281
  # Todo: warn if itime::date != shippingdate?
282

  
283
  my $correction_inventory_ids = SL::DB::Manager::StockCountingItem->get_all(where    => ['!correction_inventory_id' => undef],
284
                                                                             select   => ['correction_inventory_id'],
285
                                                                             distcint => 1);
286
  foreach my $object (@$objects) {
287
    my $start      = $object->counting->start_time_of_counting;
288
    my $inbetweens = SL::DB::Manager::Inventory->get_all(where  => [itime    => { ge => $start },
289
                                                                    parts_id => $object->part_id,
290
                                                                    bin_id   => $object->bin_id,
291
                                                                    '!id'    => [map { $_->correction_inventory_id } @$correction_inventory_ids]],
292
                                                         select => ['qty']);
293
    $object->{inbetweens} = sum map { $_->qty } @$inbetweens;
294
  }
295
}
296

  
221 297
sub setup_list_action_bar {
222 298
  my ($self) = @_;
223 299

  
templates/design40_webpages/stock_counting_reconciliation/report_bottom.html
1 1
[%- USE L %]
2 2
  [% L.paginate_controls(models=SELF.models) %]
3
  [% L.hidden_tag('counting_id', counting_id) %]
3 4
 </form>
4 5
</div>
templates/webpages/stock_counting_reconciliation/report_bottom.html
1 1
[%- USE L %]
2 2
 [% L.paginate_controls(models=SELF.models) %]
3
 [% L.hidden_tag('counting_id', counting_id) %]
3 4
</form>

Auch abrufbar als: Unified diff