Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 2401956f

Von Tamino Steinert vor fast 2 Jahren hinzugefügt

  • ID 2401956fda2e97c2b828476691f9b92e451571f8
  • Vorgänger 8fa9177a
  • Nachfolger 90b47258

WIP DispositionManager

Unterschiede anzeigen:

SL/Controller/DispositionManager.pm
use SL::DB::Part;
use SL::DB::PurchaseBasketItem;
use SL::DB::Order;
use SL::DB::OrderItem;
use SL::DB::Vendor;
use SL::PriceSource;
use SL::Locale::String qw(t8);
......
my ($self) = @_;
$::request->{layout}->add_javascripts(
'kivi.DispositionManager.js', 'kivi.PartDetail.js'
'kivi.DispositionManager.js', 'kivi.Part.js'
);
my $basket_items = SL::DB::Manager::PurchaseBasketItem->get_all(
query => [ cleared => 'F' ],
......
push @order_items, $order_item;
}
$order->add_items( @order_items );
$order->assign_attributes(orderitems => \@order_items);
$order->db->with_transaction( sub {
$order->calculate_prices_and_taxes;
SL/Controller/Part.pm
use SL::DB::PurchaseBasketItem;
use SL::DB::Shop;
use SL::Helper::Flash;
use SL::Helper::PrintOptions;
use SL::JSON;
use SL::Locale::String qw(t8);
use SL::MoreCommon qw(save_form);
......
}
}
sub action_showdetails {
my ($self, %params) = @_;
eval {
my @bindata;
my $bins = SL::DB::Manager::Bin->get_all(with_objects => ['warehouse' ]);
my %bins_by_id = map { $_->id => $_ } @$bins;
my $inventories = SL::DB::Manager::Inventory->get_all(where => [ parts_id => $self->part->id],
with_objects => ['parts', 'trans_type' ], sort_by => 'bin_id ASC');
foreach my $bin (@{ $bins }) {
$bin->{qty} = 0;
}
foreach my $inv (@{ $inventories }) {
my $bin = $bins_by_id{ $inv->bin_id };
$bin->{qty} += $inv->qty;
$bin->{unit} = $inv->parts->unit;
$bin->{reserved} = defined $inv->reserve_for_id ? 1 : 0;
}
my $sum = 0;
my $reserve_sum = 0;
for my $bin (@{ $bins }) {
push @bindata , {
'warehouse' => $bin->warehouse->forreserve ? $bin->warehouse->description.' (R)' : $bin->warehouse->description,
'description' => $bin->description,
'qty' => $bin->{qty},
'unit' => $bin->{unit},
} if $bin->{qty} != 0;
$sum += $bin->{qty};
if($bin->warehouse->forreserve || defined $bin->warehouse->{reserve_for_id}){
$reserve_sum += $bin->{qty};
}
}
# Einfacher ? $sum = $self->part->onhand
my $todate = DateTime->now_local;
my $fromdate = DateTime->now_local->add_duration(DateTime::Duration->new(years => -1));
my $average = 0;
foreach my $inv (@{ $inventories }) {
$average += abs($inv->qty) if $inv->shippingdate && $inv->trans_type->direction eq 'out' &&
DateTime->compare($inv->shippingdate,$fromdate) != -1 &&
DateTime->compare($inv->shippingdate,$todate) == -1;
}
my $openitems = SL::DB::Manager::OrderItem->get_all(where => [ parts_id => $self->part->id, 'order.closed' => 0 ],
with_objects => ['order'],);
my ($not_delivered, $ordered) = 0;
for my $openitem (@{ $openitems }) {
if($openitem -> order -> type eq 'sales_order') {
$not_delivered += $openitem->qty - $openitem->shipped_qty;
} elsif ( $openitem->order->type eq 'purchase_order' ) {
$ordered += $openitem->qty - $openitem->delivered_qty;
}
}
my $print_form = Form->new('');
my $part = $self->part;
my $stock_amounts = $self->part->get_simple_stock_sql;
$print_form->{type} = 'part';
$print_form->{printers} = SL::DB::Manager::Printer->get_all_sorted;
my $output = SL::Presenter->get->render('part/showdetails',
part => $self->part,
BINS => \@bindata,
stock_amounts => $stock_amounts,
average => $average/12,
fromdate => $fromdate,
todate => $todate,
sum => $sum,
reserve_sum => $reserve_sum,
not_delivered => $not_delivered,
ordered => $ordered,
type_beleg => $::form->{type},
type_id => $::form->{type_id},
maker_id => $::form->{maker_id},
drawing => $::form->{drawing},
print_options => SL::Helper::PrintOptions->get_print_options(
form => $print_form,
options => {dialog_name_prefix => 'print_options.',
show_headers => 1,
no_queue => 1,
no_postscript => 1,
no_opendocument => 1,
hide_language_id_print => 1,
no_html => 1},
),
);
$self->render(\$output, { layout => 0, process => 0 });
1;
} or do {
};
}
sub action_print_label {
my ($self) = @_;
# TODO: implement
return $self->render('generic/error', { layout => 1 }, label_error => t8('Not implemented yet!'));
}
sub action_export_assembly_assortment_components {
my ($self) = @_;
SL/DB/Helper/ALL.pm
use SL::DB::ProjectStatus;
use SL::DB::ProjectType;
use SL::DB::PurchaseBasket;
use SL::DB::PurchaseBasketItem;
use SL::DB::PurchaseInvoice;
use SL::DB::Reclamation;
use SL::DB::ReclamationItem;
SL/DB/Manager/Part.pm
my ($open_qty) = selectrow_query(
$::form, $class->object_class->init_db->dbh,
$query, $part_id, $part_id, $part_id
);
) || 0;
return $open_qty if $open_qty > 0;
return 0;
SL/DB/Part.pm
use SL::DB::MetaSetup::Part;
use SL::DB::Manager::Part;
use SL::DB::Chart;
use SL::DB::Vendor;
use SL::DB::Manager::Vendor;
use SL::DB::Helper::AttrHTML;
use SL::DB::Helper::AttrSorted;
use SL::DB::Helper::TransNumberGenerator;
......
__PACKAGE__->before_save('_before_save_set_partnumber');
__PACKAGE__->before_save('_before_save_set_assembly_weight');
sub init_onhandqty{
my ($self) = @_;
my $qty = SL::Helper::Inventory::get_onhand(part => $self->id);
return $qty;
}
sub init_stockqty{
my ($self) = @_;
my $qty = SL::Helper::Inventory::get_stock(part => $self->id);
return $qty;
}
sub init_get_open_ordered_qty {
my $self = shift;
my $result = SL::DB::Manager::Part->get_open_ordered_qty($self->id);
return $result;
}
sub _before_save_set_partnumber {
my ($self) = @_;
......
return $result{ $self->id };
}
sub is_parts_first_order {
my ($self, %params) = @_;
#require SL::DB::OrderItem;
my $orders_count = SL::DB::Manager::OrderItem->get_all_count( where => [ %params ], with_objects => 'order' );
return $orders_count == 1 ? 1 : 0;
}
sub available_units {
shift->unit_obj->convertible_units;
}
......
return \@vendor_dd;
}
sub init_onhandqty{
my ($self) = @_;
my $qty = SL::Helper::Inventory::get_onhand(part => $self->id) || 0;
return $qty;
}
sub init_stockqty{
my ($self) = @_;
my $qty = SL::Helper::Inventory::get_stock(part => $self->id) || 0;
return $qty;
}
sub init_get_open_ordered_qty {
my ($self) = @_;
my $result = SL::DB::Manager::Part->get_open_ordered_qty($self->id);
return $result;
}
1;
__END__
js/kivi.Part.js
$('#ic').submit();
};
ns.print_from_showdetail = function(part_id) {
var data = $('#print_options_form').serializeArray();
data.push({ name: 'action', value: 'Part/print_label' });
data.push({ name: 'part.id', value: part_id });
$.download("controller.pl", data);
};
ns.delete = function() {
var data = $('#ic').serializeArray();
data.push({ name: 'action', value: 'Part/delete' });
locale/de/all
'Normalize part description and part notes' => 'Normalisierung Artikelbeschreibung und Artikellangtext (Bemerkung)',
'Not Discountable' => 'Nicht rabattierfähig',
'Not delivered' => 'Nicht geliefert',
'Not delivered amount' => 'nicht gelieferte Menge',
'Not done yet' => 'Noch nicht fertig',
'Not enough in stock for the serial number #1' => 'Nicht genug auf Lager von der Seriennummer #1',
'Not obsolete' => 'Gültig',
......
'OrderItem' => 'Position',
'Ordered' => 'Von Kunden bestellt',
'Ordered purchase' => 'Bestellte Menge',
'Ordered, but not delivered (purchase)' => 'Bestellt, nicht geliefert(EK)',
'Orderer' => 'BestellerIn',
'Orders' => 'Aufträge',
'Orders / Delivery Orders deleteable' => 'Aufträge / Lieferscheine löschbar',
......
'Subtotals per quarter' => 'Zwischensummen pro Quartal',
'Such entries cannot be exported into the DATEV format and have to be fixed as well.' => 'Solche Einträge sind aber nicht DATEV-exportiertbar und müssen ebenfalls korrigiert werden.',
'Suggested invoice' => 'Rechnungsvorschlag',
'Sum Amount' => 'Gesamtmenge',
'Sum Credit' => 'Summe Haben',
'Sum Debit' => 'Summe Soll',
'Sum for' => 'Summe für',
......
'part \'#\'1 in bin \'#2\' only with qty #3 (need additional #4) and chargenumber \'#5\'.' => 'Artikel \'#1\' im \'#2\' nur mit der Menge #3 (noch #4 benötig) und Chargennummer \'#5\'.',
'part_list' => 'Warenliste',
'pdf_records.zip' => 'pdf_belege.zip',
'per month' => 'pro Monat',
'percental' => 'prozentual',
'periodic' => 'Aufwandsmethode',
'perpetual' => 'Bestandsmethode',
t/controllers/disposition_manager/disposition_manager.t
my $controller = SL::Controller::DispositionManager->new();
my $reorder_parts = $controller->_get_parts;
is(scalar @{$reorder_parts}, 6, "found 6 parts where onhand <= rop");
is(scalar @{$reorder_parts}, 5, "found 5 parts where onhand < rop");
# die; # die here if you want to test making basket manually
......
select $oldFH;
close $outputFH;
is(SL::DB::Manager::PurchaseBasketItem->get_all_count(), 6, "6 items in purchase basket ok");
is(SL::DB::Manager::PurchaseBasketItem->get_all_count(), 5, "5 items in purchase basket ok");
# die; # die here if you want to test creating purchase orders manually
......
my $purchase_order = SL::DB::Manager::Order->get_first();
is( scalar @{$purchase_order->items}, 6, "Purchase order has 6 item ok");
print "PART\n";
print Dumper($part1);
is( scalar @{$purchase_order->items}, 5, "Purchase order has 5 item ok");
# print "PART\n";
# print Dumper($part1);
my $first_item = $purchase_order->items_sorted->[0];
print "FIRST\n";
print Dumper($first_item);
# print "FIRST\n";
# print Dumper($first_item);
is( $first_item->parts_id, $part1->id, "Purchase order: first item is part1");
is( $first_item->qty, '1.0000000', "Purchase order: first item has qty 1");
cmp_ok( $purchase_order->netamount, '==', 52, "Purchase order: netamount ok");
is( $first_item->active_price_source, 'makemodel/' . $part1->makemodels->[0]->id . "/" . $part1->makemodels->[0]->parts_id . "/" . $part1->makemodels->[0]->make, "Purchase order: first item has correct active_price_source" . $first_item->part->partnumber);
is( $first_item->qty, '20.00000', "Purchase order: first item has qty 20");
cmp_ok( $purchase_order->netamount, '==', 240, "Purchase order: netamount ok");
is( $first_item->active_price_source, 'makemodel/' . $part1->makemodels->[0]->id, "Purchase order: first item has correct active_price_source" . $first_item->part->partnumber);
clear_up();
done_testing();
t/workflow/delivery_order_reclamation.t
"sales_delivery_order_items to sales_reclamation_items",
qw(
id delivery_order_id reclamation_id itime mtime
cusordnumber marge_price_factor ordnumber transdate
cusordnumber marge_price_factor ordnumber transdate orderer_id
description reason_description_ext reason_description_int reason_id
));
} @sales_delivery_order_items, @converted_sales_reclamation_items;
......
"sales_reclamation_items to sales_delivery_order_items",
qw(
id delivery_order_id reclamation_id itime mtime
cusordnumber marge_price_factor ordnumber transdate
cusordnumber marge_price_factor ordnumber transdate orderer_id
description reason_description_ext reason_description_int reason_id
));
} @sales_reclamation_items, @converted_sales_delivery_order_items;
......
"purchase_delivery_order_items to purchase_reclamation_items",
qw(
id delivery_order_id reclamation_id itime mtime
cusordnumber marge_price_factor ordnumber transdate
cusordnumber marge_price_factor ordnumber transdate orderer_id
description reason_description_ext reason_description_int reason_id
));
} @purchase_delivery_order_items, @converted_purchase_reclamation_items;
......
"purchase_reclamation_items to purchase_delivery_order_items",
qw(
id delivery_order_id reclamation_id itime mtime
cusordnumber marge_price_factor ordnumber transdate
cusordnumber marge_price_factor ordnumber transdate orderer_id
description reason_description_ext reason_description_int reason_id
));
} @purchase_reclamation_items, @converted_purchase_delivery_order_items;
t/workflow/order_reclamation.t
"sales_order_items to sales_reclamation_items",
qw(
id trans_id reclamation_id itime mtime
cusordnumber marge_percent marge_price_factor marge_total optional ordnumber ship subtotal transdate
cusordnumber marge_percent marge_price_factor marge_total optional ordnumber ship subtotal transdate orderer_id
reason_description_ext reason_description_int reason_id
recurring_billing_mode recurring_billing_invoice_id
));
......
"sales_reclamation_items to sales_order_items",
qw(
id trans_id reclamation_id itime mtime
cusordnumber marge_percent marge_price_factor marge_total optional ordnumber ship subtotal transdate
cusordnumber marge_percent marge_price_factor marge_total optional ordnumber ship subtotal transdate orderer_id
reason_description_ext reason_description_int reason_id
recurring_billing_mode recurring_billing_invoice_id
));
......
"purchase_order_items to purchase_reclamation_items",
qw(
id trans_id reclamation_id itime mtime
cusordnumber marge_percent marge_price_factor marge_total optional ordnumber ship subtotal transdate
cusordnumber marge_percent marge_price_factor marge_total optional ordnumber ship subtotal transdate orderer_id
reason_description_ext reason_description_int reason_id
recurring_billing_mode recurring_billing_invoice_id
));
......
"purchase_reclamation_items to purchase_order_items",
qw(
id trans_id reclamation_id itime mtime
cusordnumber marge_percent marge_price_factor marge_total optional ordnumber ship subtotal transdate
cusordnumber marge_percent marge_price_factor marge_total optional ordnumber ship subtotal transdate orderer_id
reason_description_ext reason_description_int reason_id
recurring_billing_mode recurring_billing_invoice_id
));
templates/webpages/part/showdetails.html
[%- USE LxERP -%][% USE L %][% USE HTML %][%- USE JavaScript -%][% USE T8 %][%- USE Dumper %]
<div style="padding-bottom: 15px">
<table style="width: 100%" border="0px" ><tbody>
<tr>
<td><b>[% LxERP.t8('Description') %]</b></td><td colspan="3">[% part.description %]</td>
</tr>
<tr>
<td style="background:wheat;"><b>[% LxERP.t8('Internal Notes') %]</b></td><td colspan="3" style="background:wheat;">[% part.intnotes %]</td>
</tr>
<tr>
<td><b>[% LxERP.t8('Default Warehouse') %]</b></td><td>[% part.warehouse.description %]</td>
<td><b>[% LxERP.t8('Default Bin') %]</b></td><td>[% part.bin.description %]</td>
</tr>
<tr>
<td><b>[% LxERP.t8('ROP') %]</b></td><td>[% part.rop_as_number %]</td>
</tr>
<tr>
[%- IF stock_amounts.size %]
<td colspan="4"><table style="width: 100%">
<tr class='listheading'>
<th class="listheading">[% 'Warehouse' | $T8 %]</th>
<th class="listheading">[% 'Bin' | $T8 %]</th>
<th class="listheading">[% 'Chargenumber' | $T8 %]</th>
<th class="listheading">[% 'Qty' | $T8 %]</th>
<th class="listheading">[% 'Unit' | $T8 %]</th>
</tr>
[% FOREACH stock = stock_amounts %]
<tr class='listrow'>
<td >[% HTML.escape(stock.warehouse_description) %]</td>
<td >[% IF stock.order_link %]<a target="_blank" href="[% stock.order_link %]">[% END %]
[% HTML.escape(stock.bin_description) %]
[% IF stock.order_link %]</a>[% END %]
</td>
<td >[% HTML.escape(stock.chargenumber) %]</td>
<td class='numeric'>[% LxERP.format_amount(stock.qty, dec) %]</td>
<td >[% HTML.escape(stock.unit) %]</td>
</tr>
[% IF stock.wh_lead != stock.warehouse_description %]
<tr class='listheading'>
<th class="listheading" >[% HTML.escape(stock.warehouse_description) %]</th>
<td></td>
<td></td>
<td class='numeric bold'>[% LxERP.format_amount(stock.wh_run_qty, dec) %]</td>
<td></td>
</tr>
[% END %]
[% IF loop.last %]
<tr class='listheading'>
<th class="listheading">[% 'Total' | $T8 %]</th>
<td></td>
<td></td>
<td class='numeric bold'>[% LxERP.format_amount(stock.run_qty, dec) %]</td>
<td></td>
</tr>
[% END %]
[% END %]
[% ELSE %]
<td>
<p>[% 'No transactions yet.' | $T8 %]</p>
[% END %]
</td>
</tr>
<tr>
<td><b>[% LxERP.t8('Sum Amount') %]</b></td><td>[% LxERP.format_amount(sum, 2) %] [% part.unit %]</td>
<td rowspan="5">
[% file = part.default_partimage %]
[%- IF file && INSTANCE_CONF.get_parts_show_image %]
<img src="controller.pl?action=File/download&id=[% file.id %][%- IF file.version %]&version=[%- file.version %][%- END %]" alt="[% file.title %]" style="[% INSTANCE_CONF.get_parts_image_css %]">
[% END %]
</td>
<td rowspan="5">
[%- FOREACH file = part.get_files %]
<a href="controller.pl?action=File/download&id=[% file.id %][%- IF file.version %]&version=[%- file.version %][%- END %]">
<span id="[% "filename_" _ file.id %][%- IF file.version %]_[% file.version %][%- END %]">[% file.file_name %]</span></a><br><br>
[%- END %]
</td>
</tr>
<tr>
<td><b>[% LxERP.t8('Not delivered amount') %]</b></td><td colspan="3">[% LxERP.format_amount(not_delivered, 2) %] [% part.unit %]</td></tr>
</tr>
<tr>
<td><b>[% LxERP.t8('Ordered, but not delivered (purchase)') %]</b></td><td colspan="3">[% LxERP.format_amount(ordered, 2) %] [% part.unit %]</td></tr>
</tr>
<tr>
<td><b>[% LxERP.t8('Reserved amount') %]</b></td><td colspan="3">[% LxERP.format_amount(part.stockqty - part.onhandqty, 2) %] [% part.unit %]</td></tr>
</tr>
<tr>
<td><b>[% LxERP.t8('Available amount') %]</b></td><td colspan="3">[% LxERP.format_amount(part.onhandqty, 2) %] [% part.unit %]</td></tr>
</tr>
<tr>
<td><b>[% LxERP.t8('Consume average') %]</b></td><td colspan="3">[% LxERP.format_amount(average, 2) %] [% part.unit %] [% LxERP.t8('per month') %]</td></tr>
<tr><td colspan="4" nowrap>([% LxERP.t8('in the time between') %] [% fromdate.to_kivitendo %] - [% todate.to_kivitendo %])</td>
</tr>
<tr>
<td>[%- L.button_tag("return \$('#detail_menu').dialog('close');", LxERP.t8('Close Details'), class => "submit") %]</td>
</tr>
</tbody></table>
</div>
<div id="print_options" >
<form id="print_options_form">
[% print_options %]
<br>
[% L.button_tag('kivi.Part.print_from_showdetail(' _ part.id _ ')', LxERP.t8('Print')) %]
</form>
</div>

Auch abrufbar als: Unified diff