Merge branch 'test' of ../kivitendo-erp_20220811
[kivitendo-erp.git] / bin / mozilla / ic.pl
index d54e83d..f3e7fe5 100644 (file)
@@ -41,6 +41,7 @@ use SL::CVar;
 use SL::IC;
 use SL::Helper::Flash qw(flash);
 use SL::HTML::Util;
+use SL::Presenter::Part;
 use SL::ReportGenerator;
 
 #use SL::PE;
@@ -84,9 +85,6 @@ sub search {
   $form->{lastsort}     = ""; # memory for which table was sort at last time
   $form->{ndxs_counter} = 0;  # counter for added entries to top100
 
-  # for seach all possibibilities, is_service only used as UNLESS so == 0
-  my %is_xyz     = ("is_part" => 1, "is_service" => 0, "is_assembly" =>1 );
-
   $form->{title} = (ucfirst $form->{searchitems}) . "s";
   $form->{title} =~ s/ys$/ies/;
   $form->{title} = $locale->text($form->{title});
@@ -97,91 +95,15 @@ sub search {
                                                                            'include_prefix' => 'l_',
                                                                            'include_value'  => 'Y');
 
+  setup_ic_search_action_bar();
   $form->header;
 
   $form->get_lists('partsgroup'    => 'ALL_PARTSGROUPS');
-  print $form->parse_html_template('ic/search', { %is_xyz, });
-
-  $lxdebug->leave_sub();
-}    #end search()
-
-sub search_update_prices {
-  $lxdebug->enter_sub();
-
-  $auth->assert('part_service_assembly_edit');
-
-  my $pricegroups = IC->get_pricegroups(\%myconfig, \%$form);
-
-  $form->{title} = $locale->text('Update Prices');
-
-  $form->header;
-
-  print $form->parse_html_template('ic/search_update_prices', { PRICE_ROWS => $pricegroups });
+  print $form->parse_html_template('ic/search');
 
   $lxdebug->leave_sub();
 }    #end search()
 
-sub confirm_price_update {
-  $lxdebug->enter_sub();
-
-  $auth->assert('part_service_assembly_edit');
-
-  my @errors      = ();
-  my $value_found = undef;
-
-  foreach my $idx (qw(sellprice listprice), (1..$form->{price_rows})) {
-    my $name      = $idx =~ m/\d/ ? $form->{"pricegroup_${idx}"}      : $idx eq 'sellprice' ? $locale->text('Sell Price') : $locale->text('List Price');
-    my $type      = $idx =~ m/\d/ ? $form->{"pricegroup_type_${idx}"} : $form->{"${idx}_type"};
-    my $value_idx = $idx =~ m/\d/ ? "price_${idx}" : $idx;
-    my $value     = $form->parse_amount(\%myconfig, $form->{$value_idx});
-
-    if ((0 > $value) && ($type eq 'percent')) {
-      push @errors, $locale->text('You cannot adjust the price for pricegroup "#1" by a negative percentage.', $name);
-
-    } elsif (!$value && ($form->{$value_idx} ne '')) {
-      push @errors, $locale->text('No valid number entered for pricegroup "#1".', $name);
-
-    } elsif (0 < $value) {
-      $value_found = 1;
-    }
-  }
-
-  push @errors, $locale->text('No prices will be updated because no prices have been entered.') if (!$value_found);
-
-  my $num_matches = IC->get_num_matches_for_priceupdate();
-
-  $form->header();
-
-  if (@errors) {
-    $form->show_generic_error(join('<br>', @errors), 'back_button' => 1);
-  }
-
-  $form->{nextsub} = "update_prices";
-
-  map { delete $form->{$_} } qw(action header);
-
-  print $form->parse_html_template('ic/confirm_price_update', { HIDDENS     => [ map { name => $_, value => $form->{$_} }, keys %$form ],
-                                                                num_matches => $num_matches });
-
-  $lxdebug->leave_sub();
-}
-
-sub update_prices {
-  $lxdebug->enter_sub();
-
-  $auth->assert('part_service_assembly_edit');
-
-  my $num_updated = IC->update_prices(\%myconfig, \%$form);
-
-  if (-1 != $num_updated) {
-    $form->redirect($locale->text('#1 prices were updated.', $num_updated));
-  } else {
-    $form->error($locale->text('Could not update prices!'));
-  }
-
-  $lxdebug->leave_sub();
-}
-
 sub top100 {
   $::lxdebug->enter_sub();
 
@@ -243,18 +165,9 @@ sub generate_report {
 
   my $cvar_configs = CVar->get_configs('module' => 'IC');
 
-  my %titles = (
-    ''         => $locale->text('Articles'),
-    part       => $locale->text('Parts'),
-    service    => $locale->text('Services'),
-    assembly   => $locale->text('Assemblies'),
-    assortment => $locale->text('Assortments'),
-  );
-
-  $form->{title} = $titles{$form->{searchitems}};
+  $form->{title} = $locale->text('Articles');
 
   my %column_defs = (
-    'bin'                => { 'text' => $locale->text('Bin'), },
     'deliverydate'       => { 'text' => $locale->text('deliverydate'), },
     'description'        => { 'text' => $locale->text('Part Description'), },
     'notes'              => { 'text' => $locale->text('Notes'), },
@@ -264,6 +177,7 @@ sub generate_report {
     'insertdate'         => { 'text' => $locale->text('Insert Date'), },
     'invnumber'          => { 'text' => $locale->text('Invoice Number'), },
     'lastcost'           => { 'text' => $locale->text('Last Cost'), },
+    'assembly_lastcost'  => { 'text' => $locale->text('Assembly Last Cost'), },
     'linetotallastcost'  => { 'text' => $locale->text('Extended'), },
     'linetotallistprice' => { 'text' => $locale->text('Extended'), },
     'linetotalsellprice' => { 'text' => $locale->text('Extended'), },
@@ -271,23 +185,28 @@ sub generate_report {
     'microfiche'         => { 'text' => $locale->text('Microfiche'), },
     'name'               => { 'text' => $locale->text('Name'), },
     'onhand'             => { 'text' => $locale->text('Stocked Qty'), },
+    'assembly_qty'       => { 'text' => $locale->text('Assembly Item Qty'), },
     'ordnumber'          => { 'text' => $locale->text('Order Number'), },
     'partnumber'         => { 'text' => $locale->text('Part Number'), },
     'partsgroup'         => { 'text' => $locale->text('Partsgroup'), },
-    'priceupdate'        => { 'text' => $locale->text('Updated'), },
+    'priceupdate'        => { 'text' => $locale->text('Price updated'), },
     'quonumber'          => { 'text' => $locale->text('Quotation'), },
     'rop'                => { 'text' => $locale->text('ROP'), },
     'sellprice'          => { 'text' => $locale->text('Sell Price'), },
     'serialnumber'       => { 'text' => $locale->text('Serial Number'), },
     'soldtotal'          => { 'text' => $locale->text('Qty in Selected Records'), },
     'name'               => { 'text' => $locale->text('Name in Selected Records'), },
-    'transdate'          => { 'text' => $locale->text('Transdate'), },
+    'transdate'          => { 'text' => $locale->text('Transdate Record'), },
     'unit'               => { 'text' => $locale->text('Unit'), },
     'weight'             => { 'text' => $locale->text('Weight'), },
     'shop'               => { 'text' => $locale->text('Shop article'), },
     'type_and_classific' => { 'text' => $locale->text('Type'), },
     'projectnumber'      => { 'text' => $locale->text('Project Number'), },
     'projectdescription' => { 'text' => $locale->text('Project Description'), },
+    'warehouse'          => { 'text' => $locale->text('Default Warehouse'), },
+    'bin'                => { 'text' => $locale->text('Default Bin'), },
+    'make'               => { 'text' => $locale->text('Make'), },
+    'model'              => { 'text' => $locale->text('Model'), },
   );
 
   $revers     = $form->{revers};
@@ -350,6 +269,7 @@ sub generate_report {
     obsolete      => $locale->text('Obsolete'),
     orphaned      => $locale->text('Orphaned'),
     onhand        => $locale->text('On Hand'),
+    assembly_qty  => $locale->text('Assembly Item Qty'),
     short         => $locale->text('Short'),
     onorder       => $locale->text('On Order'),
     ordered       => $locale->text('Ordered'),
@@ -366,17 +286,23 @@ sub generate_report {
     description   => $locale->text('Part Description') . ": '$form->{description}'",
     make          => $locale->text('Make')             . ": '$form->{make}'",
     model         => $locale->text('Model')            . ": '$form->{model}'",
+    customername  => $locale->text('Customer')         . ": '$form->{customername}'",
+    customernumber=> $locale->text('Customer Part Number').": '$form->{customernumber}'",
     drawing       => $locale->text('Drawing')          . ": '$form->{drawing}'",
     microfiche    => $locale->text('Microfiche')       . ": '$form->{microfiche}'",
     l_soldtotal   => $locale->text('Qty in Selected Records'),
     ean           => $locale->text('EAN')              . ": '$form->{ean}'",
     insertdatefrom => $locale->text('Insert Date') . ": " . $locale->text('From')       . " " . $locale->date(\%myconfig, $form->{insertdatefrom}, 1),
     insertdateto   => $locale->text('Insert Date') . ": " . $locale->text('To (time)')  . " " . $locale->date(\%myconfig, $form->{insertdateto}, 1),
+    l_service     => $locale->text('Services'),
+    l_assembly    => $locale->text('Assemblies'),
+    l_part        => $locale->text('Parts'),
   );
 
   my @itemstatus_keys = qw(active obsolete orphaned onhand short);
   my @callback_keys   = qw(onorder ordered rfq quoted bought sold partnumber partsgroup partsgroup_id serialnumber description make model
-                           drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto insertdatefrom insertdateto ean shop all);
+                           drawing microfiche l_soldtotal l_deliverydate transdatefrom transdateto insertdatefrom insertdateto ean shop all
+                           l_service l_assembly l_part);
 
   # calculate dependencies
   for (@itemstatus_keys, @callback_keys) {
@@ -398,6 +324,7 @@ sub generate_report {
     $column_defs{sellprice}{text} = $locale->text('Price');
     $form->{l_lastcost} = ""
   }
+  $form->{l_assembly_lastcost} = "Y" if $form->{l_assembly} && $form->{l_lastcost};
 
   if ($form->{description}) {
     $description = $form->{description};
@@ -414,8 +341,8 @@ sub generate_report {
 
   if ($form->{l_service} && !$form->{l_assembly} && !$form->{l_part}) {
 
-    # remove bin, weight and rop from list
-    map { $form->{"l_$_"} = "" } qw(bin weight rop);
+    # remove warehouse, bin, weight and rop from list
+    map { $form->{"l_$_"} = "" } qw(bin weight rop warehouse);
 
     $form->{l_onhand} = "";
 
@@ -451,6 +378,10 @@ sub generate_report {
 
     flash('warning', $::locale->text('Soldtotal does not make sense without any bsooqr options'));
   }
+  if ($form->{l_soldtotal} && ($form->{l_warehouse} || $form->{l_bin})) {
+    delete $form->{"l_$_"} for  qw(bin warehouse);
+    flash('warning', $::locale->text('Sorry, I am too stupid to figure out the default warehouse/bin and the sold qty. I drop the default warehouse/bin option.'));
+  }
   if ($form->{l_name} && !$bsooqr_mode) {
     delete $form->{l_name};
 
@@ -459,8 +390,9 @@ sub generate_report {
   IC->all_parts(\%myconfig, \%$form);
 
   my @columns = qw(
-    partnumber type_and_classific description notes partsgroup bin onhand rop soldtotal unit listprice
-    linetotallistprice sellprice linetotalsellprice lastcost linetotallastcost
+    partnumber type_and_classific description notes partsgroup warehouse bin
+    make model assembly_qty onhand rop soldtotal unit listprice
+    linetotallistprice sellprice linetotalsellprice lastcost assembly_lastcost linetotallastcost
     priceupdate weight image drawing microfiche invnumber ordnumber quonumber
     transdate name serialnumber deliverydate ean projectnumber projectdescription
     insertdate shop
@@ -488,14 +420,16 @@ sub generate_report {
 
   %column_defs = (%column_defs, %column_defs_cvars, %column_defs_pricegroups);
   map { $column_defs{$_}->{visible} ||= $form->{"l_$_"} ? 1 : 0 } @columns;
-  map { $column_defs{$_}->{align}   = 'right' } qw(onhand sellprice listprice lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal shop), @pricegroup_columns;
+  map { $column_defs{$_}->{align}   = 'right' } qw(assembly_qty onhand sellprice listprice lastcost assembly_lastcost linetotalsellprice linetotallastcost linetotallistprice rop weight soldtotal shop), @pricegroup_columns;
 
   my @hidden_variables = (
     qw(l_subtotal l_linetotal searchitems itemstatus bom l_pricegroups insertdatefrom insertdateto),
-    qw(l_type_and_classific classification_id),
+    qw(l_type_and_classific classification_id l_part l_service l_assembly l_assortment),
     @itemstatus_keys,
     @callback_keys,
     map({ "cvar_$_->{name}" } @searchable_custom_variables),
+    map({'cvar_'. $_->{name} .'_from'} grep({$_->{type} eq 'date'} @searchable_custom_variables)),
+    map({'cvar_'. $_->{name} .'_to'}   grep({$_->{type} eq 'date'} @searchable_custom_variables)),
     map({'cvar_'. $_->{name} .'_qtyop'} grep({$_->{type} eq 'number'} @searchable_custom_variables)),
     map({ "l_$_" } @columns),
   );
@@ -503,7 +437,7 @@ sub generate_report {
   my $callback         = build_std_url('action=generate_report', grep { $form->{$_} } @hidden_variables);
 
   my @sort_full        = qw(partnumber description onhand soldtotal deliverydate insertdate shop);
-  my @sort_no_revers   = qw(partsgroup bin priceupdate invnumber ordnumber quonumber name image drawing serialnumber);
+  my @sort_no_revers   = qw(partsgroup invnumber ordnumber quonumber name image drawing serialnumber);
 
   foreach my $col (@sort_full) {
     $column_defs{$col}->{link} = join '&', $callback, "sort=$col", map { "$_=" . E($form->{$_}) } qw(revers lastsort);
@@ -571,6 +505,7 @@ sub generate_report {
     $ref->{sellprice}     *= $ref->{exchangerate} / $ref->{price_factor};
     $ref->{listprice}     *= $ref->{exchangerate} / $ref->{price_factor};
     $ref->{lastcost}      *= $ref->{exchangerate} / $ref->{price_factor};
+    $ref->{assembly_lastcost} *= $ref->{exchangerate} / $ref->{price_factor};
 
     # use this for assemblies
     my $soldtotal = $bsooqr_mode ? $ref->{soldtotal} : $ref->{onhand};
@@ -581,11 +516,11 @@ sub generate_report {
       $soldtotal                  = 0 if ($form->{sold});
     }
 
-    my $edit_link               = build_std_url('script=controller.pl', 'action=Part/edit', 'part.id=' . E($ref->{id}), 'callback');
+    my $edit_link               = build_std_url('script=controller.pl', 'action=Part/edit', 'part.id=' . E($ref->{id}));
     $row->{partnumber}->{link}  = $edit_link;
     $row->{description}->{link} = $edit_link;
 
-    foreach (qw(sellprice listprice lastcost)) {
+    foreach (qw(sellprice listprice lastcost assembly_lastcost)) {
       $row->{$_}{data}            = $form->format_amount(\%myconfig, $ref->{$_}, 2);
       $row->{"linetotal$_"}{data} = $form->format_amount(\%myconfig, $ref->{onhand} * $ref->{$_}, 2);
     }
@@ -619,8 +554,17 @@ sub generate_report {
       # | ist bestellt  | Von Kunden bestellt |  -> edit_oe_ord_link
       # | Anfrage       | Angebot             |  -> edit_oe_quo_link
 
-      my $edit_oe_ord_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'purchase_order' : 'sales_order'), 'id=' . E($ref->{trans_id}), 'callback');
-      my $edit_oe_quo_link = build_std_url("script=oe.pl", 'action=edit', 'type=' . E($ref->{cv} eq 'vendor' ? 'request_quotation' : 'sales_quotation'), 'id=' . E($ref->{trans_id}), 'callback');
+      my $edit_oe_ord_link = ($::instance_conf->get_feature_experimental_order)
+                           ? build_std_url("script=controller.pl", 'action=Order/edit',
+                                           'type=' . E($ref->{cv} eq 'vendor' ? 'purchase_order' : 'sales_order'),        'id=' . E($ref->{trans_id}), 'callback')
+                           : build_std_url("script=oe.pl",         'action=edit',
+                                           'type=' . E($ref->{cv} eq 'vendor' ? 'purchase_order' : 'sales_order'),        'id=' . E($ref->{trans_id}), 'callback');
+
+      my $edit_oe_quo_link = ($::instance_conf->get_feature_experimental_order)
+                           ? build_std_url("script=controller.pl", 'action=Order/edit',
+                                           'type=' . E($ref->{cv} eq 'vendor' ? 'request_quotation' : 'sales_quotation'), 'id=' . E($ref->{trans_id}), 'callback')
+                           : build_std_url("script=oe.pl",         'action=edit',
+                                           'type=' . E($ref->{cv} eq 'vendor' ? 'request_quotation' : 'sales_quotation'), 'id=' . E($ref->{trans_id}), 'callback');
 
       $row->{ordnumber}{link} = $edit_oe_ord_link;
       $row->{quonumber}{link} = $edit_oe_quo_link if (!$ref->{ordnumber});
@@ -637,8 +581,11 @@ sub generate_report {
     map { $row->{$_}{link} = $ref->{$_} } qw(drawing microfiche);
 
     $row->{notes}{data} = SL::HTML::Util->strip($ref->{notes});
-    $row->{type_and_classific}{data} = $::request->presenter->type_abbreviation($ref->{part_type}).
-                                       $::request->presenter->classification_abbreviation($ref->{classification_id});
+    $row->{type_and_classific}{data} = SL::Presenter::Part::type_abbreviation($ref->{part_type}).
+                                       SL::Presenter::Part::classification_abbreviation($ref->{classification_id});
+
+    # last price update
+    $row->{priceupdate}{data} = SL::DB::Part->new(id => $ref->{id})->load->last_price_update->valid_from->to_kivitendo;
 
     $report->add_data($row);
 
@@ -674,49 +621,58 @@ sub generate_report {
     $report->add_data($row);
   }
 
+  setup_ic_generate_report_action_bar();
   $report->generate_with_headers();
 
   $lxdebug->leave_sub();
 }    #end generate_report
 
-sub ajax_autocomplete {
-  $main::lxdebug->enter_sub();
-
-  my $form     = $main::form;
-  my %myconfig = %main::myconfig;
-
-  $form->{column}          = 'description'     unless $form->{column} =~ /^partnumber|description$/;
-  $form->{$form->{column}} = $form->{q}           || '';
-  $form->{limit}           = ($form->{limit} * 1) || 10;
-  $form->{searchitems}   ||= '';
-
-  my @results = IC->all_parts(\%myconfig, $form);
-
-  print $form->ajax_response_header(),
-        $form->parse_html_template('ic/ajax_autocomplete');
-
-  $main::lxdebug->leave_sub();
-}
-
-sub back_to_record {
-  _check_io_auth();
-
-
-  delete @{$::form}{qw(action action_add action_back_to_record back_sub description item notes partnumber sellprice taxaccount2 unit vc)};
-
-  $::auth->restore_form_from_session($::form->{previousform}, clobber => 1);
-  $::form->{rowcount}--;
-  $::form->{action}   = 'display_form';
-  $::form->{callback} = $::form->{script} . '?' . join('&', map { $::form->escape($_) . '=' . $::form->escape($::form->{$_}) } sort keys %{ $::form });
-  $::form->redirect;
+sub setup_ic_search_action_bar {
+  my %params = @_;
+
+  for my $bar ($::request->layout->get('actionbar')) {
+    $bar->add(
+      action => [
+        t8('Search'),
+        submit    => [ '#form', { action => 'generate_report' } ],
+        accesskey => 'enter',
+      ],
+
+      action => [
+        t8('TOP100'),
+        submit => [ '#form', { action => 'top100' } ],
+      ],
+    );
+  }
 }
 
-sub continue { call_sub($form->{"nextsub"}); }
-
-sub dispatcher {
-  my $action = first { $::form->{"action_${_}"} } qw(add back_to_record);
-  $::form->error($::locale->text('No action defined.')) unless $action;
-
-  $::form->{dispatched_action} = $action;
-  call_sub($action);
+sub setup_ic_generate_report_action_bar {
+  my %params = @_;
+
+  for my $bar ($::request->layout->get('actionbar')) {
+    $bar->add(
+      combobox => [
+        action => [
+          t8('Add'),
+        ],
+        action => [
+          t8('Add Part'),
+          submit    => [ '#new_form', { action => 'Part/add_part' } ],
+          accesskey => 'enter',
+        ],
+        action => [
+          t8('Add Service'),
+          submit    => [ '#new_form', { action => 'Part/add_service' } ],
+        ],
+        action => [
+          t8('Add Assembly'),
+          submit    => [ '#new_form', { action => 'Part/add_assembly' } ],
+        ],
+        action => [
+          t8('Add Assortment'),
+          submit    => [ '#new_form', { action => 'Part/add_assortment' } ],
+        ],
+      ], # end of combobox "Add part"
+    );
+  }
 }