]> wagnertech.de Git - kivitendo-erp.git/commitdiff
Neue Maske: Auftragsartikelsuche
authorG. Richardson <information@kivitendo-premium.de>
Tue, 9 Aug 2016 10:12:36 +0000 (12:12 +0200)
committerG. Richardson <information@kivitendo-premium.de>
Mon, 17 Oct 2016 16:05:08 +0000 (18:05 +0200)
um schnell Positionen aus (alten) Verkaufsaufträgen zu finden:

Verkauf -> Berichte -> Auftragsartikelsuche

Dies ist kein druckbarer Bericht, sondern soll helfen, schnell einen
bestimmten Auftrag oder eine Information zu einer bestimmten verkauften
Ware zu finden.

Wurde die Ware per Lieferschein verschickt und ausgelagert wird auch der
Lieferschein und die verschickte Menge angezeigt. Dies klappt aber nur
für Aufträge, wo die Einzelpositionen per RecordLinks verknüpft sind.

SL/Controller/OrderItem.pm [new file with mode: 0644]
css/common.css
doc/changelog
locale/de/all
menus/user/00-erp.yaml
templates/webpages/order_items_search/_order_item_list.html [new file with mode: 0644]
templates/webpages/order_items_search/order_items.html [new file with mode: 0644]

diff --git a/SL/Controller/OrderItem.pm b/SL/Controller/OrderItem.pm
new file mode 100644 (file)
index 0000000..7cdfb35
--- /dev/null
@@ -0,0 +1,125 @@
+package SL::Controller::OrderItem;
+
+use strict;
+
+use parent qw(SL::Controller::Base);
+use SL::DB::Order;
+use SL::DB::OrderItem;
+use SL::DB::Customer;
+use SL::DB::Part;
+use SL::Controller::Helper::GetModels;
+use SL::Controller::Helper::ParseFilter;
+use SL::Locale::String qw(t8);
+
+__PACKAGE__->run_before('check_auth');
+
+use Rose::Object::MakeMethods::Generic (
+  'scalar'                => [ qw(orderitems) ],
+  'scalar --get_set_init' => [ qw(model) ],
+);
+
+my %sort_columns = (
+  partnumber        => t8('Part Number'),
+  ordnumber         => t8('Order'),
+  customer          => t8('Customer'),
+  transdate         => t8('Date'),
+);
+
+sub action_search {
+
+  my ($self, %params) = @_;
+
+  my $title = t8("Sold order items");
+
+  $::request->layout->use_javascript('client_js.js');
+
+  # The actual loading of orderitems happens in action_order_item_list_dynamic_table
+  # which is processed inside this template and automatically called upon
+  # loading. This causes all filtered orderitems to be displayed,
+  # there is no paginate mechanism or export
+  $self->render('order_items_search/order_items', { layout => 1, process => 1 },
+                                                  title         => $title,
+               );
+}
+
+
+sub action_order_item_list_dynamic_table {
+  my ($self) = @_;
+
+  $self->orderitems( $self->model->get );
+
+
+  $self->add_linked_delivery_order_items;
+
+  $self->render('order_items_search/_order_item_list', { layout  => 0 , process => 1 });
+}
+
+sub add_linked_delivery_order_items {
+  my ($self) = @_;
+
+  my $qty_round = 2;
+
+  foreach my $orderitem ( @{ $self->orderitems } ) {
+    my $dois = $orderitem->linked_delivery_order_items;
+    $orderitem->{deliveryorders} = join('<br>', map { $_->displayable_delivery_order_info($qty_round) } @{$dois});
+  };
+};
+
+sub init_model {
+  my ($self) = @_;
+
+  SL::Controller::Helper::GetModels->new(
+    controller => $self,
+    model      => 'OrderItem',
+    query      => [ SL::DB::Manager::Order->type_filter('sales_order') ],
+    sorted       => {
+      _default     => {
+        by           => 'transdate',
+        dir          => 0,
+      },
+      %sort_columns,
+    } ,
+    with_objects    => [ 'order', 'order.customer', 'part' ],
+  );
+}
+
+sub check_auth {
+  $::auth->assert('sales_order_edit');
+}
+
+1;
+
+__END__
+
+=encoding utf-8
+
+=head1 NAME
+
+SL::Controller::OrderItem - Controller for OrderItems
+
+=head2 OVERVIEW
+
+Controller for quickly finding orderitems in sales orders. For example the
+customer phones you, saying he would like to order another one of the green
+thingies he ordered 2 years ago. You have no idea what he is referring to, but
+you can quickly filter by customer (a customerpicker) and e.g. part description
+or partnumber or order date, successively narrowing down the search. The
+resulting list is updated dynamically after keypresses.
+
+=head1 Usage
+
+Certain fields can be preset by passing them as get parameters in the URL, so
+you could create links to this report:
+
+ controller.pl?action=OrderItem/search&ordnumber=24
+ controller.pl?action=OrderItem/search&customer_id=3455
+
+=head1 TODO AND CAVEATS
+
+=over 4
+
+=item * amount of results is limited
+
+=back
+
+=cut
index fe764b82eba513a3f1fced5455f107c039348083..8893580880246a62a036879dc8e78a99224eaf18 100644 (file)
@@ -72,3 +72,6 @@ input.grow_on_focus:focus { width: 150px }
 #dunning_invoice_list .direct_debit a {
   color: #aaa;
 }
+/* orderitems */
+.shipped     { color: green }
+.not_shipped { color: red   }
index b0e70fb65b038dd79d53394663df196d1116b029..2b77b59a51bf920c7052c37abe8429b6d4ecd9b9 100644 (file)
@@ -41,6 +41,10 @@ kleinere neue Features und Detailverbesserungen:
   - Neuer Controller für Preisgruppen, die nun sortiert und ungültig gesetzt
     werden können.
 
+  - Neuer Berichte "Auftragsartikelsuche", um schnell Autragspositionen aus
+    Verkaufsauträge finden zu können:
+    Verkauf -> Berichte -> Auftragsartikelsuche
+
 Administrative Änderungen
 
   - Diverse Textsuchen werden jetzt durch eine neue Klasse Indizes
index 3dfa0390d3e4e298aadf4e362c648ff5a74c692c..c91d6be29973512a1350f72c13670f7c9bb536e3 100755 (executable)
@@ -1942,6 +1942,7 @@ $self->{texts} = {
   'Order Number missing!'       => 'Auftragsnummer fehlt!',
   'Order amount'                => 'Auftragswert',
   'Order deleted!'              => 'Auftrag gelöscht!',
+  'Order item search'           => 'Auftragsartikelsuche',
   'Order probability'           => 'Auftragswahrscheinlichkeit',
   'Order probability & expected billing date' => 'Auftragswahrscheinlichkeit & vorrauss. Abrechnungsdatum',
   'Order value periodicity'     => 'Auftragswert basiert auf Periodizität',
@@ -2570,6 +2571,7 @@ $self->{texts} = {
   'Show follow ups...'          => 'Zeige Wiedervorlagen...',
   'Show help text'              => 'Hilfetext anzeigen',
   'Show history'                => 'Verlauf anzeigen',
+  'Show images'                 => 'Bilder zeigen',
   'Show items from invoices individually' => 'Artikel aus Rechnungen anzeigen',
   'Show mappings (csv_import)'  => 'Spaltenzuordnungen anzeigen',
   'Show old dunnings'           => 'Alte Mahnungen anzeigen',
@@ -2607,6 +2609,7 @@ $self->{texts} = {
   'Skonto information'          => 'Skonto Information',
   'So far you could use one partnumber for severel parts, for example a service and an article.' => 'Bisher war es möglich eine Artikelnummer für mehrere Artikel zu verwenden, zum Beispiel eine Artikelnummer für eine Dienstleistung, eine Ware und ein Erzeugnis.',
   'Sold'                        => 'Verkauft',
+  'Sold order items'            => 'Verkaufte Auftragsartikel',
   'Soldtotal does not make sense without any bsooqr options' => 'Option "Menge in gewählten Belegen" ohne gewählte Belege wird ignoriert.',
   'Solution'                    => 'Lösung',
   'Sort By'                     => 'Sortiert nach',
index 5f2af65726929f1a2f13e5f04868a12f6378c7a6..5ef1f7f1dc062430b54e92d312c4d7b94be1396b 100644 (file)
   module: dn.pl
   params:
     action: search
+- parent: ar_reports
+  id: ar_order_item_search
+  name: Order item search
+  order: 750
+  access: sales_order_edit
+  params:
+    action: OrderItem/search
 - parent: ar_reports
   id: ar_reports_delivery_plan
   name: Delivery Plan
diff --git a/templates/webpages/order_items_search/_order_item_list.html b/templates/webpages/order_items_search/_order_item_list.html
new file mode 100644 (file)
index 0000000..3a66857
--- /dev/null
@@ -0,0 +1,40 @@
+[%- USE LxERP %]
+[%- USE T8 %]
+[%- USE L %]
+[%- USE HTML %]
+[%- USE P %]
+[% SET qty_round = 2 %]
+<table cellpadding="3px">
+ <tr class="listheading">
+  <th>[%- LxERP.t8("Part")           %]</th>
+  <th>[%- LxERP.t8("Customer")       %]</th>
+  <th>[%- LxERP.t8("Order")          %]</th>
+  <th>[%- LxERP.t8("Transdate")      %]</th>
+  <th>[%- LxERP.t8("Qty")            %]</th>
+  <th>[%- LxERP.t8("Delivered")      %]</th>
+  <th>[%- LxERP.t8("Price")          %]</th>
+  <th>[%- LxERP.t8("Discount")       %] %</th>
+  <th>[%- LxERP.t8("Delivery Order") %]</th>
+  [% IF FORM.show_images %]
+  <th>[%- LxERP.t8("Image")          %]</th>
+  [% END %]
+ </tr>
+ [% FOREACH order_item = SELF.orderitems %]
+ <tr id="tr_[% loop.count %]" class="listrow[% loop.count % 2 %]">
+  <td>                 [% P.part(order_item.part, no_link => 0)               %]</td>
+  <td>                 [% P.customer(order_item.order.customer, no_link => 0) %]</td>
+  <td class="numeric"> [% P.sales_order(order_item.order, no_link => 0)       %]</td>
+  <td>                 [% order_item.order.transdate.to_kivitendo             %]</td>
+  <td class="numeric [% IF order_item.delivered_qty == order_item.qty %]shipped[% ELSE %]not_shipped[% END %]">
+    [% LxERP.format_amount(order_item.qty, qty_round) %] [% order_item.unit | html %]
+  </td>
+  <td class="numeric"> [% LxERP.format_amount(order_item.delivered_qty, qty_round) %] [% order_item.unit | html %] </td>
+  <td class="numeric"> [% order_item.sellprice_as_number                      %]</td>
+  <td class="numeric"> [% order_item.discount_as_percent                      %]</td>
+  <td>                 [% order_item.deliveryorders                           %]</td>
+  [% IF FORM.show_images %]
+  <td> [% IF order_item.part.image %]<a href="[% order_item.part.image | html %]" target="_blank"><img height="32" border="0" src="[% order_item.part.image | html %]"/></a>[% END %]</td>
+  [% END %]
+ </tr>
+ [% END %]
+</table>
diff --git a/templates/webpages/order_items_search/order_items.html b/templates/webpages/order_items_search/order_items.html
new file mode 100644 (file)
index 0000000..8bcfa92
--- /dev/null
@@ -0,0 +1,92 @@
+[% USE HTML %]
+[%- USE LxERP %]
+[%- USE T8 %]
+[%- USE L %]
+
+[% SET size=50 %]
+[% SET show_images=0 %]
+
+<h1>[% title %]</h1>
+<div style="padding-bottom: 15px">
+[% 'Filter' | $T8 %]:
+<form id="filter" name="filter" method="post" action="controller.pl">
+ <table>
+  </tr>
+    <td>[% 'Customer' | $T8 %]</td>
+    <td>[% L.customer_vendor_picker('filter.order.customer.id', FORM.customer_id, type='customer', class="filter", size=size) %]</td>
+  </tr>
+  <tr>
+    <td>[% 'Part' | $T8 %]</td>
+    <td>[% L.input_tag('filter.part.all:substr:multi::ilike', FORM.part, size = size, class="filter") %]</td>
+  </tr>
+  <tr>
+    <td>[% 'Order Number' | $T8 %]</td>
+    <td>[% L.input_tag('filter.order.ordnumber:substr::ilike', FORM.ordnumber, size = 10, class="filter") %]</td>
+  <tr>
+  <tr>
+    <td>[% 'Order Date' | $T8 %]</td>
+    <td>[% 'From' | $T8 %] [% L.date_tag("filter.order.transdate:date::ge", filter.order.transdate_date___ge, class="filter") %] [% 'Until' | $T8 %] [% L.date_tag('filter.order.transdate:date::le', filter.order.transdate_date__le, class="filter") %]</td>
+  <tr>
+  <tr>
+    <td>[% 'Description' | $T8 %]</td>
+    <td>[% L.input_tag('filter.description:substr::ilike', filter.description_substr__ilike, size = size, class="filter") %]</td>
+  </tr>
+  <tr>
+    <td>[% 'Long Description' | $T8 %]</td>
+    <td>[% L.input_tag('filter.longdescription:substr::ilike', filter.longdescription_substr__ilike, size = size, class="filter") %]  </tr>
+  <tr>
+    <td>[% 'Show images' | $T8 %]</td>
+    <td>[% L.checkbox_tag('show_images', checked=show_images) %]  </tr>
+  </tr>
+</table>
+[% L.button_tag("this.form.reset(); refresh_plot();", LxERP.t8("Reset")) %]
+</form>
+
+<div id="orderitems" style="padding-top: 20px">
+[% PROCESS 'order_items_search/_order_item_list.html' %]
+</div>
+
+
+<script type="text/javascript">
+  $(function() {
+    [% IF FORM.customer_id %]
+      $( "#filter_part_all_substr_multi_ilike" ).focus();
+    [% ELSE %]
+      $( "#filter_order_customer_id_name" ).focus();
+    [% END %]
+
+    addInputCallback($(".filter"), refresh_plot , 300 );
+
+    $('#show_images').change(function(){
+      refresh_plot();
+    });
+  });
+
+
+  function refresh_plot() {
+    var filterdata = $('#filter').serialize()
+    var url = './controller.pl?action=OrderItem/order_item_list_dynamic_table&' + filterdata;
+    $.ajax({
+        url : url,
+        type: 'POST',
+        success: function(data){
+            $('#orderitems').html(data);
+        }
+    })
+
+  };
+
+function addInputCallback(inputfield, callback, delay) {
+    var timer = null;
+    inputfield.on('keyup', function() {
+        if (timer) {
+            window.clearTimeout(timer);
+        }
+        timer = window.setTimeout( function() {
+            timer = null;
+            callback();
+        }, delay );
+    });
+    inputfield = null;
+}
+</script>