74 |
74 |
|
75 |
75 |
my @sortable = qw(reqdate customer vendor ordnumber partnumber description);
|
76 |
76 |
|
77 |
|
# if csv report export no units (better calculation in
|
|
77 |
# if csv report export no units
|
78 |
78 |
my $rp_csv_mod = ($csv_option eq 'CSV') ? 1 : '';
|
79 |
79 |
|
80 |
80 |
my %column_defs = (
|
81 |
|
reqdate => { sub => sub { $_[0]->reqdate_as_date || $_[0]->order->reqdate_as_date } },
|
82 |
|
description => { sub => sub { $_[0]->description },
|
83 |
|
obj_link => sub { $self->link_to($_[0]->part) } },
|
84 |
|
partnumber => { sub => sub { $_[0]->part->partnumber },
|
85 |
|
obj_link => sub { $self->link_to($_[0]->part) } },
|
|
81 |
reqdate => { sub => sub { $_[0]->reqdate_as_date || $_[0]->order->reqdate_as_date } },
|
|
82 |
description => { sub => sub { $_[0]->description },
|
|
83 |
obj_link => sub { $self->link_to($_[0]->part) } },
|
|
84 |
partnumber => { sub => sub { $_[0]->part->partnumber },
|
|
85 |
obj_link => sub { $self->link_to($_[0]->part) } },
|
86 |
86 |
qty => { sub => sub { $_[0]->qty_as_number .
|
87 |
|
($rp_csv_mod ? '' : ' ' . $_[0]->unit) } },
|
|
87 |
($rp_csv_mod ? '' : ' ' . $_[0]->unit) } },
|
88 |
88 |
netto_qty => { sub => sub { $::form->format_amount(\%::myconfig,
|
89 |
89 |
($_[0]->qty * $_[0]->sellprice * (1 - $_[0]->discount) /
|
90 |
|
($_[0]->price_factor || 1), 2)) },},
|
91 |
|
unit => { sub => sub { $_[0]->unit },
|
92 |
|
visible => $rp_csv_mod },
|
|
90 |
($_[0]->price_factor || 1), 2)) },},
|
|
91 |
unit => { sub => sub { $_[0]->unit },
|
|
92 |
visible => $rp_csv_mod },
|
93 |
93 |
shipped_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]{shipped_qty}, 2) .
|
94 |
|
($rp_csv_mod ? '' : ' ' . $_[0]->unit) } },
|
95 |
|
netto_shipped_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]{netto_shipped_qty}, 2) },},
|
96 |
|
not_shipped_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]->qty - $_[0]{shipped_qty} - $_[0]{delivered_qty} - $_[0]{do_closed_qty}, 2) .
|
97 |
|
($rp_csv_mod ? '' : ' ' . $_[0]->unit) } },
|
|
94 |
($rp_csv_mod ? '' : ' ' . $_[0]->unit) } },
|
|
95 |
netto_shipped_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]{netto_shipped_qty}, 2) },},
|
|
96 |
not_shipped_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]->qty - $_[0]{shipped_qty}
|
|
97 |
- $_[0]{delivered_qty} - $_[0]{do_closed_qty}, 2) .
|
|
98 |
($rp_csv_mod ? '' : ' ' . $_[0]->unit) } },
|
98 |
99 |
delivered_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]{delivered_qty}, 2) .
|
99 |
|
($rp_csv_mod ? '' : ' ' . $_[0]->unit) } },
|
100 |
|
netto_delivered_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]{netto_delivered_qty}, 2) },},
|
101 |
|
|
102 |
|
netto_not_shipped_qty => { sub => sub { $::form->format_amount(\%::myconfig,(
|
103 |
|
($_[0]->qty - $_[0]{shipped_qty} - $_[0]{delivered_qty} - $_[0]{do_closed_qty})
|
104 |
|
* ($_[0]->sellprice * (1 - $_[0]->discount) / ($_[0]->price_factor || 1)), 2)) },},
|
|
100 |
($rp_csv_mod ? '' : ' ' . $_[0]->unit) } },
|
|
101 |
netto_delivered_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]{netto_delivered_qty}, 2) },},
|
|
102 |
netto_not_shipped_qty => { sub => sub { $::form->format_amount(\%::myconfig,(($_[0]->qty -
|
|
103 |
$_[0]{shipped_qty} - $_[0]{delivered_qty} - $_[0]{do_closed_qty})
|
|
104 |
* ($_[0]->sellprice * (1 - $_[0]->discount) /
|
|
105 |
($_[0]->price_factor || 1)), 2)) },},
|
105 |
106 |
do_closed_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]{do_closed_qty}, 2) .
|
106 |
|
($rp_csv_mod ? '' : ' ' . $_[0]->unit) },},
|
107 |
|
|
108 |
|
netto_do_closed_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]{netto_do_closed_qty}, 2) },
|
109 |
|
visible => $mode eq 'delivery_value_report' },
|
110 |
|
ordnumber => { sub => sub { $_[0]->order->ordnumber },
|
111 |
|
obj_link => sub { $self->link_to($_[0]->order) } },
|
112 |
|
vendor => { sub => sub { $_[0]->order->vendor->name },
|
|
107 |
($rp_csv_mod ? '' : ' ' . $_[0]->unit) },},
|
|
108 |
netto_do_closed_qty => { sub => sub { $::form->format_amount(\%::myconfig, $_[0]{netto_do_closed_qty}, 2) },},
|
|
109 |
ordnumber => { sub => sub { $_[0]->order->ordnumber },
|
|
110 |
obj_link => sub { $self->link_to($_[0]->order) } },
|
|
111 |
vendor => { sub => sub { $_[0]->order->vendor->name },
|
113 |
112 |
visible => $vc eq 'vendor',
|
114 |
|
obj_link => sub { $self->link_to($_[0]->order->vendor) } },
|
115 |
|
customer => { sub => sub { $_[0]->order->customer->name },
|
|
113 |
obj_link => sub { $self->link_to($_[0]->order->vendor) } },
|
|
114 |
customer => { sub => sub { $_[0]->order->customer->name },
|
116 |
115 |
visible => $vc eq 'customer',
|
117 |
|
obj_link => sub { $self->link_to($_[0]->order->customer) } },
|
|
116 |
obj_link => sub { $self->link_to($_[0]->order->customer) } },
|
118 |
117 |
);
|
119 |
118 |
|
120 |
119 |
$column_defs{$_}->{text} = $sort_columns{$_} for keys %column_defs;
|
... | ... | |
123 |
122 |
std_column_visibility => 1,
|
124 |
123 |
controller_class => 'DeliveryValueReport',
|
125 |
124 |
output_format => 'HTML',
|
126 |
|
top_info_text => ($vc eq 'customer') ? (($mode eq 'delivery_plan') ? $::locale->text('Delivery Plan for currently outstanding sales orders') :
|
127 |
|
$::locale->text('Delivery Value Report for currently open sales orders')) :
|
128 |
|
($mode eq 'delivery_value_report') ? $::locale->text('Delivery Value Report for currently outstanding purchase orders') :
|
129 |
|
$::locale->text('Delivery Plan for currently outstanding purchase orders'),
|
130 |
|
title => ($mode eq 'delivery_plan') ? $::locale->text('Delivery Plan') : $::locale->text('Delivery Value Report'),
|
|
125 |
top_info_text => ($vc eq 'customer') ? t8('Delivery Value Report for currently open sales orders') :
|
|
126 |
t8('Delivery Value Report for currently outstanding purchase orders'),
|
|
127 |
title => $::locale->text('Delivery Value Report'),
|
131 |
128 |
allow_pdf_export => 1,
|
132 |
129 |
allow_csv_export => 1,
|
133 |
130 |
);
|
... | ... | |
154 |
151 |
sub make_filter_summary {
|
155 |
152 |
my ($self) = @_;
|
156 |
153 |
my $vc = $self->vc;
|
157 |
|
my $mode = 'delivery_value_report';
|
158 |
154 |
my ($business, $employee);
|
159 |
155 |
|
160 |
156 |
my $filter = $::form->{filter} || {};
|
... | ... | |
179 |
175 |
[ $employee, $::locale->text('Employee') ],
|
180 |
176 |
);
|
181 |
177 |
|
|
178 |
# flags for with_object 'part'
|
182 |
179 |
my %flags = (
|
183 |
180 |
part => $::locale->text('Parts'),
|
184 |
181 |
service => $::locale->text('Services'),
|
... | ... | |
203 |
200 |
sub init_models {
|
204 |
201 |
my ($self) = @_;
|
205 |
202 |
my $vc = $self->vc;
|
206 |
|
$main::lxdebug->message(0, 'vc hier:' . $vc);
|
207 |
203 |
SL::Controller::Helper::GetModels->new(
|
208 |
204 |
controller => $self,
|
209 |
205 |
model => 'OrderItem',
|
... | ... | |
214 |
210 |
},
|
215 |
211 |
%sort_columns,
|
216 |
212 |
},
|
217 |
|
# show only open orders
|
218 |
|
query => [ 'order.closed' => '0', "order.${vc}_id" => { gt => 0 } ],
|
219 |
|
with_objects => [ 'order', "order.$vc" ],
|
|
213 |
# show only open (sales|purchase) orders
|
|
214 |
query => [ 'order.closed' => '0', "order.${vc}_id" => { gt => 0 },
|
|
215 |
'order.quotation' => 0 ],
|
|
216 |
with_objects => [ 'order', "order.$vc", 'part' ],
|
220 |
217 |
additional_url_params => { vc => $vc},
|
221 |
|
);
|
|
218 |
)
|
222 |
219 |
}
|
223 |
220 |
|
224 |
221 |
sub init_vc {
|
225 |
|
return $::form->{vc} if ($::form->{vc} eq 'customer' || $::form->{vc} eq 'vendor') || croak "self (DeliveryPlan) has no vc defined";
|
|
222 |
return $::form->{vc} if ($::form->{vc} eq 'customer' || $::form->{vc} eq 'vendor') || croak "self (DeliveryValueReport) has no vc defined";
|
226 |
223 |
}
|
227 |
224 |
sub init_all_employees {
|
228 |
225 |
return SL::DB::Manager::Employee->get_all_sorted;
|
... | ... | |
259 |
256 |
sub calc_qtys_price {
|
260 |
257 |
my ($self, $orderitems) = @_;
|
261 |
258 |
# using $orderitem->shipped_qty 40 times is far too slow. need to do it manually
|
262 |
|
#
|
|
259 |
# also for calc net values
|
263 |
260 |
|
264 |
261 |
return unless scalar @$orderitems;
|
265 |
262 |
|
266 |
263 |
my %orderitems_by_id = map { $_->id => $_ } @$orderitems;
|
267 |
264 |
|
268 |
265 |
my $query = <<SQL;
|
269 |
|
SELECT oi.id, doi.qty, doi.qty, doi.unit, doe.delivered, doe.closed, -- dois.qty as qty_stocked,
|
|
266 |
SELECT oi.id, doi.qty, doi.unit, doe.delivered, doe.closed,
|
270 |
267 |
oi.sellprice, oi.discount, oi.price_factor
|
271 |
268 |
FROM record_links rl
|
272 |
269 |
INNER JOIN delivery_order_items doi ON (doi.id = rl.to_id)
|
273 |
270 |
INNER JOIN orderitems oi ON (oi.id = rl.from_id)
|
274 |
271 |
INNER JOIN delivery_orders doe ON (doe.id = doi.delivery_order_id)
|
275 |
|
--INNER JOIN delivery_order_items_stock dois ON (doi.id = dois.delivery_order_item_id)
|
276 |
272 |
WHERE rl.from_table = 'orderitems'
|
277 |
273 |
AND rl.to_table = 'delivery_order_items'
|
278 |
274 |
AND oi.id IN (@{[ join ', ', ("?")x @$orderitems ]})
|
... | ... | |
285 |
281 |
$item->{shipped_qty} ||= 0;
|
286 |
282 |
$item->{delivered_qty} ||= 0;
|
287 |
283 |
$item->{do_closed_qty} ||= 0;
|
288 |
|
$item->{shipped_qty} += AM->convert_unit($row->{unit} => $item->unit) * $row->{qty} unless ($row->{delivered} || $row->{closed});
|
289 |
|
$item->{delivered_qty} += AM->convert_unit($row->{unit} => $item->unit) * $row->{qty} if ($row->{delivered} && !$row->{closed});
|
290 |
|
$item->{do_closed_qty} += AM->convert_unit($row->{unit} => $item->unit) * $row->{qty} if ($row->{closed});
|
291 |
|
#$item->{do_closed_qty} += AM->convert_unit($row->{unit} => $item->unit) * $row->{qty_stocked} if ($row->{closed});
|
|
284 |
$item->{shipped_qty} += AM->convert_unit($row->{unit} => $item->unit) * $row->{qty} unless ($row->{delivered} || $row->{closed});
|
|
285 |
$item->{delivered_qty} += AM->convert_unit($row->{unit} => $item->unit) * $row->{qty} if ($row->{delivered} && !$row->{closed});
|
|
286 |
$item->{do_closed_qty} += AM->convert_unit($row->{unit} => $item->unit) * $row->{qty} if ($row->{closed});
|
292 |
287 |
$item->{not_shipped_qty} += AM->convert_unit($row->{unit} => $item->unit) * $row->{qty} unless ($row->{delivered});
|
293 |
|
# my $price_factor = $self->price_factor || 1;
|
294 |
|
#$self->_delivered_qty;
|
295 |
|
#$item->{netto_qty} += $row->{qty} * $row->{sellprice} * (1 - $row->{discount} ) / ($row->{price_factor} || 1);
|
296 |
|
# no sum
|
297 |
|
$item->{netto_shipped_qty} = $item->{shipped_qty} * $row->{sellprice} * (1 - $row->{discount} ) / ($row->{price_factor} || 1);
|
298 |
|
# $item->{netto_not_shipped_qty} += $item->{shipped_qty} * $row->{sellprice} * (1 - $row->{discount} ) / ($row->{price_factor} || 1);
|
299 |
|
$item->{netto_delivered_qty} = $item->{delivered_qty} * $row->{sellprice} * (1 - $row->{discount} ) / ($row->{price_factor} || 1);
|
300 |
|
$item->{netto_do_closed_qty} = $item->{do_closed_qty} * $row->{sellprice} * (1 - $row->{discount} ) / ($row->{price_factor} || 1);
|
|
288 |
|
|
289 |
my $price_factor = $row->{price_factor} || 1;
|
|
290 |
$item->{netto_shipped_qty} = $item->{shipped_qty} * $row->{sellprice} * (1 - $row->{discount} ) / $price_factor;
|
|
291 |
$item->{netto_delivered_qty} = $item->{delivered_qty} * $row->{sellprice} * (1 - $row->{discount} ) / $price_factor;
|
|
292 |
$item->{netto_do_closed_qty} = $item->{do_closed_qty} * $row->{sellprice} * (1 - $row->{discount} ) / $price_factor;
|
301 |
293 |
|
302 |
294 |
}
|
303 |
295 |
}
|
... | ... | |
342 |
334 |
other metadata for the position has been altered. This is due to the fact that a hidden converted_from_previous_document is
|
343 |
335 |
used in the position.
|
344 |
336 |
|
345 |
|
from_table | id | to_table | id
|
346 |
|
orderitems | 7 | delivery_order_items | 11
|
|
337 |
The main intelligence is this query (qty_stocked as comments):
|
|
338 |
|
|
339 |
SELECT oi.id,and more metadata , -- dois.qty as qty_stocked,
|
|
340 |
FROM record_links rl
|
|
341 |
INNER JOIN delivery_order_items doi ON (doi.id = rl.to_id)
|
|
342 |
INNER JOIN orderitems oi ON (oi.id = rl.from_id)
|
|
343 |
INNER JOIN delivery_orders doe ON (doe.id = doi.delivery_order_id)
|
|
344 |
--INNER JOIN delivery_order_items_stock dois ON (doi.id = dois.delivery_order_item_id)
|
|
345 |
WHERE rl.from_table = 'orderitems'
|
|
346 |
AND rl.to_table = 'delivery_order_items'
|
|
347 |
|
|
348 |
Get all entries which were converted from orderitems to delivery_order_items (WHERE).
|
|
349 |
The persistent id are in rl, therefore we can fetch orderitems and delivery_order_items.
|
|
350 |
The join on delivery_orders (doe) is only needed for the current state of the delivery order (closed, delivered).
|
347 |
351 |
|
348 |
352 |
=head1 FUNCTIONS
|
349 |
353 |
|
350 |
354 |
=over 2
|
351 |
355 |
|
352 |
|
=item C<action_list_sales_delivery_orders>
|
|
356 |
=item C<action_list>
|
|
357 |
|
|
358 |
=item C<prepare_report>
|
|
359 |
|
|
360 |
=item C<make_filter_summary>
|
|
361 |
|
|
362 |
=item C<calc_qtys_price>
|
|
363 |
|
|
364 |
=item C<link_to>
|
|
365 |
|
|
366 |
=item C<init_models>
|
|
367 |
|
|
368 |
=item C<init_vc>
|
|
369 |
|
|
370 |
=item C<init_all_employees>
|
|
371 |
|
|
372 |
=item C<init_all_businesses>
|
|
373 |
|
|
374 |
=back
|
|
375 |
|
|
376 |
=head1 TODOS
|
|
377 |
|
|
378 |
Currently no foreign currencies and OrderItems with taxincluded are calculated / supported. The report can be easily extended
|
|
379 |
for the real stocked qty. The report is really easy to implement and customise if your model is focussed straight.
|
|
380 |
For long term maintaineance it would be wise to add more testcases for the conversion from orders to delivery_orders.
|
|
381 |
Right now record_links are tested only from document to document and the convert_invoice method (via task server) has a
|
|
382 |
test case with record_links items included. Furhtermore I personally dislike the calcs in the %columns_def, but for a quick report
|
|
383 |
this is ok, though if we redesign this further, the taxincluded / currency cases should be implemented as well.
|
|
384 |
|
|
385 |
|
|
386 |
=head1 AUTHOR
|
|
387 |
|
|
388 |
Jan Büren E<lt>jan@kivitendo-premium.deE<gt> (based on DeliveryPlan.pm by Sven)
|
353 |
389 |
|
354 |
|
L
|
|
390 |
=cut
|
Changelog, Kosmetik und Warenfilter für Lieferwertbericht
Doku und changelog ergänzt. Sinnvoller formatiert und model
um part erweitert, damit der Filter (Erzeugnis, Dienstleistung und
Ware) wie beim Lieferplan funktioniert.