Kunden-/Lieferantenstammdaten: Berechtigungsmodell gefixt
[kivitendo-erp.git] / SL / Controller / CustomerVendor.pm
1 package SL::Controller::CustomerVendor;
2
3 use strict;
4 use parent qw(SL::Controller::Base);
5
6 use List::MoreUtils qw(any);
7
8 use SL::JSON;
9 use SL::DBUtils;
10 use SL::Helper::Flash;
11 use SL::Locale::String;
12 use SL::Util qw(trim);
13 use SL::Webdav;
14 use SL::ZUGFeRD;
15 use SL::Controller::Helper::GetModels;
16 use SL::Controller::Helper::ReportGenerator;
17 use SL::Controller::Helper::ParseFilter;
18
19 use SL::DB::Customer;
20 use SL::DB::Vendor;
21 use SL::DB::Business;
22 use SL::DB::ContactDepartment;
23 use SL::DB::ContactTitle;
24 use SL::DB::Employee;
25 use SL::DB::Greeting;
26 use SL::DB::Language;
27 use SL::DB::TaxZone;
28 use SL::DB::Note;
29 use SL::DB::PaymentTerm;
30 use SL::DB::Pricegroup;
31 use SL::DB::Price;
32 use SL::DB::Contact;
33 use SL::DB::FollowUp;
34 use SL::DB::FollowUpLink;
35 use SL::DB::History;
36 use SL::DB::Currency;
37 use SL::DB::Invoice;
38 use SL::DB::PurchaseInvoice;
39 use SL::DB::Order;
40
41 use Data::Dumper;
42
43 use Rose::Object::MakeMethods::Generic (
44   scalar                  => [ qw(user_has_edit_rights) ],
45   'scalar --get_set_init' => [ qw(customer_models vendor_models zugferd_settings) ],
46 );
47
48 # safety
49 __PACKAGE__->run_before(
50   '_instantiate_args',
51   only => [
52     'save',
53     'save_and_ap_transaction',
54     'save_and_ar_transaction',
55     'save_and_close',
56     'save_and_invoice',
57     'save_and_order',
58     'save_and_quotation',
59     'save_and_rfq',
60     'delete',
61     'delete_contact',
62     'delete_shipto',
63   ]
64 );
65
66 __PACKAGE__->run_before(
67   '_load_customer_vendor',
68   only => [
69     'edit',
70     'show',
71     'update',
72     'ajaj_get_shipto',
73     'ajaj_get_contact',
74     'ajax_list_prices',
75   ]
76 );
77
78 # make sure this comes after _load_customer_vendor
79 __PACKAGE__->run_before('_check_auth');
80
81 __PACKAGE__->run_before(
82   '_create_customer_vendor',
83   only => [
84     'add',
85   ]
86 );
87
88 __PACKAGE__->run_before('normalize_name');
89
90
91 sub action_add {
92   my ($self) = @_;
93
94   $self->_pre_render();
95   $self->{cv}->assign_attributes(hourly_rate => $::instance_conf->get_customer_hourly_rate) if $self->{cv}->is_customer;
96
97   $self->render(
98     'customer_vendor/form',
99     title => ($self->is_vendor() ? $::locale->text('Add Vendor') : $::locale->text('Add Customer')),
100     %{$self->{template_args}}
101   );
102 }
103
104 sub action_edit {
105   my ($self) = @_;
106
107   $self->_pre_render();
108   $self->render(
109     'customer_vendor/form',
110     title => ($self->is_vendor() ? $::locale->text('Edit Vendor') : $::locale->text('Edit Customer')),
111     %{$self->{template_args}}
112   );
113 }
114
115 sub action_show {
116   my ($self) = @_;
117
118   if ($::request->type eq 'json') {
119     my $cv_hash;
120     if (!$self->{cv}) {
121       # TODO error
122     } else {
123       $cv_hash          = $self->{cv}->as_tree;
124       $cv_hash->{cvars} = $self->{cv}->cvar_as_hashref;
125     }
126
127     $self->render(\ SL::JSON::to_json($cv_hash), { layout => 0, type => 'json', process => 0 });
128   }
129 }
130
131 sub _save {
132   my ($self) = @_;
133
134   my @errors = $self->{cv}->validate;
135   if (@errors) {
136     flash('error', @errors);
137     $self->_pre_render();
138     $self->render(
139       'customer_vendor/form',
140       title => ($self->is_vendor() ? t8('Edit Vendor') : t8('Edit Customer')),
141       %{$self->{template_args}}
142     );
143     $::dispatcher->end_request;
144   }
145
146   $self->{cv}->greeting(trim $self->{cv}->greeting);
147   my $save_greeting           = $self->{cv}->greeting
148     && $::instance_conf->get_vc_greetings_use_textfield
149     && SL::DB::Manager::Greeting->get_all_count(where => [description => $self->{cv}->greeting]) == 0;
150
151   $self->{contact}->cp_title(trim($self->{contact}->cp_title));
152   my $save_contact_title      = $self->{contact}->cp_title
153     && $::instance_conf->get_contact_titles_use_textfield
154     && SL::DB::Manager::ContactTitle->get_all_count(where => [description => $self->{contact}->cp_title]) == 0;
155
156   $self->{contact}->cp_abteilung(trim($self->{contact}->cp_abteilung));
157   my $save_contact_department = $self->{contact}->cp_abteilung
158     && $::instance_conf->get_contact_departments_use_textfield
159     && SL::DB::Manager::ContactDepartment->get_all_count(where => [description => $self->{contact}->cp_abteilung]) == 0;
160
161   my $db = $self->{cv}->db;
162
163   $db->with_transaction(sub {
164     my $cvs_by_nr;
165     if ( $self->is_vendor() ) {
166       if ( $self->{cv}->vendornumber ) {
167         $cvs_by_nr = SL::DB::Manager::Vendor->get_all(query => [vendornumber => $self->{cv}->vendornumber]);
168       }
169     } else {
170       if ( $self->{cv}->customernumber ) {
171         $cvs_by_nr = SL::DB::Manager::Customer->get_all(query => [customernumber => $self->{cv}->customernumber]);
172       }
173     }
174
175     foreach my $entry (@{$cvs_by_nr}) {
176       if( $entry->id != $self->{cv}->id ) {
177         my $msg =
178           $self->is_vendor() ? $::locale->text('This vendor number is already in use.') : $::locale->text('This customer number is already in use.');
179
180         $::form->error($msg);
181       }
182     }
183
184     $self->{cv}->save(cascade => 1);
185
186     SL::DB::Greeting->new(description => $self->{cv}->greeting)->save if $save_greeting;
187
188     $self->{contact}->cp_cv_id($self->{cv}->id);
189     if( $self->{contact}->cp_name ne '' || $self->{contact}->cp_givenname ne '' ) {
190       SL::DB::ContactTitle     ->new(description => $self->{contact}->cp_title)    ->save if $save_contact_title;
191       SL::DB::ContactDepartment->new(description => $self->{contact}->cp_abteilung)->save if $save_contact_department;
192
193       $self->{contact}->save(cascade => 1);
194     }
195
196     if( $self->{note}->subject ne '' && $self->{note}->body ne '' ) {
197
198       if ( !$self->{note_followup}->follow_up_date ) {
199         $::form->error($::locale->text('Date missing!'));
200       }
201
202       $self->{note}->trans_id($self->{cv}->id);
203       $self->{note}->save();
204
205       $self->{note_followup}->save();
206
207       $self->{note_followup_link}->follow_up_id($self->{note_followup}->id);
208       $self->{note_followup_link}->trans_id($self->{cv}->id);
209       $self->{note_followup_link}->save();
210
211       SL::Helper::Flash::flash_later('info', $::locale->text('Follow-Up saved.'));
212     }
213
214     $self->{shipto}->trans_id($self->{cv}->id);
215     if(any { $self->{shipto}->$_ ne '' } qw(shiptoname shiptodepartment_1 shiptodepartment_2 shiptostreet shiptozipcode shiptocity shiptocountry shiptogln shiptocontact shiptophone shiptofax shiptoemail)) {
216       $self->{shipto}->save(cascade => 1);
217     }
218
219     my $snumbers = $self->is_vendor() ? 'vendornumber_'. $self->{cv}->vendornumber : 'customernumber_'. $self->{cv}->customernumber;
220     SL::DB::History->new(
221       trans_id => $self->{cv}->id,
222       snumbers => $snumbers,
223       employee_id => SL::DB::Manager::Employee->current->id,
224       addition => 'SAVED',
225     )->save();
226
227     if ( $::form->{delete_notes} ) {
228       foreach my $note_id (@{ $::form->{delete_notes} }) {
229         my $note = SL::DB::Note->new(id => $note_id)->load();
230         if ( $note->follow_up ) {
231           if ( $note->follow_up->follow_up_link ) {
232             $note->follow_up->follow_up_link->delete(cascade => 'delete');
233           }
234           $note->follow_up->delete(cascade => 'delete');
235         }
236         $note->delete(cascade => 'delete');
237       }
238     }
239
240     1;
241   }) || die($db->error);
242
243 }
244
245 sub action_save {
246   my ($self) = @_;
247
248   $self->_save();
249
250   my @redirect_params = (
251     action => 'edit',
252     id     => $self->{cv}->id,
253     db     => ($self->is_vendor() ? 'vendor' : 'customer'),
254   );
255
256   if ( $self->{contact}->cp_id ) {
257     push(@redirect_params, contact_id => $self->{contact}->cp_id);
258   }
259
260   if ( $self->{shipto}->shipto_id ) {
261     push(@redirect_params, shipto_id => $self->{shipto}->shipto_id);
262   }
263
264   $self->redirect_to(@redirect_params);
265 }
266
267 sub action_save_and_close {
268   my ($self) = @_;
269
270   $self->_save();
271
272   my $msg = $self->is_vendor() ? $::locale->text('Vendor saved') : $::locale->text('Customer saved');
273   $::form->redirect($msg);
274 }
275
276 sub _transaction {
277   my ($self, $script) = @_;
278
279   $::auth->assert('gl_transactions | ap_transactions | ar_transactions'.
280                     '| invoice_edit         | vendor_invoice_edit | ' .
281                  ' request_quotation_edit | sales_quotation_edit | sales_order_edit    | purchase_order_edit');
282
283   $self->_save();
284
285   my $name = $::form->escape($self->{cv}->name, 1);
286   my $db = $self->is_vendor() ? 'vendor' : 'customer';
287   my $action = 'add';
288
289   if ($::instance_conf->get_feature_experimental_order && 'oe.pl' eq $script) {
290     $script = 'controller.pl';
291     $action = 'Order/' . $action;
292   }
293
294   my $url = $self->url_for(
295     controller => $script,
296     action     => $action,
297     vc         => $db,
298     $db .'_id' => $self->{cv}->id,
299     $db        => $name,
300     type       => $::form->{type},
301     callback   => $::form->{callback},
302   );
303
304   print $::form->redirect_header($url);
305 }
306
307 sub action_save_and_ar_transaction {
308   my ($self) = @_;
309
310   $main::auth->assert('ar_transactions');
311
312   $self->_transaction('ar.pl');
313 }
314
315 sub action_save_and_ap_transaction {
316   my ($self) = @_;
317
318   $main::auth->assert('ap_transactions');
319
320   $self->_transaction('ap.pl');
321 }
322
323 sub action_save_and_invoice {
324   my ($self) = @_;
325
326   if ( $self->is_vendor() ) {
327     $::auth->assert('vendor_invoice_edit');
328   } else {
329     $::auth->assert('invoice_edit');
330   }
331
332   $::form->{type} = 'invoice';
333   $self->_transaction($self->is_vendor() ? 'ir.pl' : 'is.pl');
334 }
335
336 sub action_save_and_order {
337   my ($self) = @_;
338
339   if ( $self->is_vendor() ) {
340     $::auth->assert('purchase_order_edit');
341   } else {
342     $::auth->assert('sales_order_edit');
343   }
344
345   $::form->{type} = $self->is_vendor() ? 'purchase_order' : 'sales_order';
346   $self->_transaction('oe.pl');
347 }
348
349 sub action_save_and_rfq {
350   my ($self) = @_;
351
352   $::auth->assert('request_quotation_edit');
353
354   $::form->{type} = 'request_quotation';
355   $self->_transaction('oe.pl');
356 }
357
358 sub action_save_and_quotation {
359   my ($self) = @_;
360
361   $::auth->assert('sales_quotation_edit');
362
363   $::form->{type} = 'sales_quotation';
364   $self->_transaction('oe.pl');
365 }
366
367 sub action_delete {
368   my ($self) = @_;
369
370   my $db = $self->{cv}->db;
371
372   if( !$self->is_orphaned() ) {
373     $self->action_edit();
374   } else {
375
376     $db->with_transaction(sub {
377       $self->{cv}->delete(cascade => 1);
378
379       my $snumbers = $self->is_vendor() ? 'vendornumber_'. $self->{cv}->vendornumber : 'customernumber_'. $self->{cv}->customernumber;
380       SL::DB::History->new(
381         trans_id => $self->{cv}->id,
382         snumbers => $snumbers,
383         employee_id => SL::DB::Manager::Employee->current->id,
384         addition => 'DELETED',
385       )->save();
386     }) || die($db->error);
387
388     my $msg = $self->is_vendor() ? $::locale->text('Vendor deleted!') : $::locale->text('Customer deleted!');
389     $::form->redirect($msg);
390   }
391
392 }
393
394
395 sub action_delete_contact {
396   my ($self) = @_;
397
398   my $db = $self->{contact}->db;
399
400   if ( !$self->{contact}->cp_id ) {
401     SL::Helper::Flash::flash('error', $::locale->text('No contact selected to delete'));
402   } else {
403
404     $db->with_transaction(sub {
405       if ( $self->{contact}->used ) {
406         $self->{contact}->detach();
407         $self->{contact}->save();
408         SL::Helper::Flash::flash('info', $::locale->text('Contact is in use and was flagged invalid.'));
409       } else {
410         $self->{contact}->delete(cascade => 1);
411         SL::Helper::Flash::flash('info', $::locale->text('Contact deleted.'));
412       }
413
414       1;
415     }) || die($db->error);
416
417     $self->{contact} = $self->_new_contact_object;
418   }
419
420   $self->action_edit();
421 }
422
423 sub action_delete_shipto {
424   my ($self) = @_;
425
426   my $db = $self->{shipto}->db;
427
428   if ( !$self->{shipto}->shipto_id ) {
429     SL::Helper::Flash::flash('error', $::locale->text('No shipto selected to delete'));
430   } else {
431
432     $db->with_transaction(sub {
433       if ( $self->{shipto}->used ) {
434         $self->{shipto}->detach();
435         $self->{shipto}->save(cascade => 1);
436         SL::Helper::Flash::flash('info', $::locale->text('Shipto is in use and was flagged invalid.'));
437       } else {
438         $self->{shipto}->delete(cascade => 1);
439         SL::Helper::Flash::flash('info', $::locale->text('Shipto deleted.'));
440       }
441
442       1;
443     }) || die($db->error);
444
445     $self->{shipto} = SL::DB::Shipto->new();
446   }
447
448   $self->action_edit();
449 }
450
451
452 sub action_search {
453   my ($self) = @_;
454
455   my @url_params = (
456     controller => 'ct.pl',
457     action => 'search',
458     db => $self->is_vendor() ? 'vendor' : 'customer',
459   );
460
461   if ( $::form->{callback} ) {
462     push(@url_params, callback => $::form->{callback});
463   }
464
465   $self->redirect_to(@url_params);
466 }
467
468
469 sub action_search_contact {
470   my ($self) = @_;
471
472   my $url = 'ct.pl?action=search_contact&db=customer';
473
474   if ( $::form->{callback} ) {
475     $url .= '&callback='. $::form->escape($::form->{callback});
476   }
477
478   print $::form->redirect_header($url);
479 }
480
481 sub action_get_delivery {
482   my ($self) = @_;
483
484   $::auth->assert('sales_all_edit')    if $self->is_customer();
485   $::auth->assert('purchase_all_edit') if $self->is_vendor();
486
487   my $dbh = $::form->get_standard_dbh();
488
489   my ($arap, $db, $qty_sign);
490   if ( $self->is_vendor() ) {
491     $arap = 'ap';
492     $db = 'vendor';
493     $qty_sign = ' * -1 AS qty';
494   } else {
495     $arap = 'ar';
496     $db = 'customer';
497     $qty_sign = '';
498   }
499
500   my $where = ' WHERE 1=1';
501   my @values;
502
503   if ( !$self->is_vendor() && $::form->{shipto_id} && $::form->{shipto_id} ne 'all' ) {
504     $where .= " AND ${arap}.shipto_id = ?";
505     push(@values, $::form->{shipto_id});
506   } else {
507     $where .= " AND ${arap}.${db}_id = ?";
508     push(@values, $::form->{id});
509   }
510
511   if ( $::form->{delivery_from} ) {
512     $where .= " AND ${arap}.transdate >= ?";
513     push(@values, conv_date($::form->{delivery_from}));
514   }
515
516   if ( $::form->{delivery_to} ) {
517     $where .= " AND ${arap}.transdate <= ?";
518     push(@values, conv_date($::form->{delivery_to}));
519   }
520
521   my $query =
522     "SELECT
523        s.shiptoname,
524        i.qty ${qty_sign},
525        ${arap}.id,
526        ${arap}.transdate,
527        ${arap}.invnumber,
528        ${arap}.ordnumber,
529        i.description,
530        i.unit,
531        i.sellprice,
532        oe.id AS oe_id,
533        invoice
534      FROM ${arap}
535
536      LEFT JOIN shipto s
537       ON ". ($arap eq 'ar' ? '(ar.shipto_id = s.shipto_id) ' : '(ap.id = s.trans_id) ') ."
538
539      LEFT JOIN invoice i
540        ON ${arap}.id = i.trans_id
541
542      LEFT JOIN parts p
543        ON p.id = i.parts_id
544
545      LEFT JOIN oe
546        ON (oe.ordnumber = ${arap}.ordnumber AND NOT ${arap}.ordnumber = ''
547            AND ". ($arap eq 'ar' ? 'oe.customer_id IS NOT NULL' : 'oe.vendor_id IS NOT NULL') ." )
548
549      ${where}
550      ORDER BY ${arap}.transdate DESC LIMIT 15";
551
552   $self->{delivery} = selectall_hashref_query($::form, $dbh, $query, @values);
553
554   $self->render('customer_vendor/get_delivery', { layout => 0 });
555 }
556
557 sub action_ajaj_get_shipto {
558   my ($self) = @_;
559
560   my $data = {};
561   $data->{shipto} = {
562     map(
563       {
564         my $name = 'shipto'. $_;
565         $name => $self->{shipto}->$name;
566       }
567       qw(_id name department_1 department_2 street zipcode city gln country contact phone fax email)
568     )
569   };
570
571   $data->{shipto_cvars} = $self->_prepare_cvar_configs_for_ajaj($self->{shipto}->cvars_by_config);
572
573   $self->render(\SL::JSON::to_json($data), { type => 'json', process => 0 });
574 }
575
576 sub action_ajaj_get_contact {
577   my ($self) = @_;
578
579   my $data;
580
581   $data->{contact} = {
582     map(
583       {
584         my $name = 'cp_'. $_;
585
586         if ( $_ eq 'birthday' && $self->{contact}->$name ) {
587           $name => $self->{contact}->$name->to_lxoffice;
588         } else {
589           $name => $self->{contact}->$name;
590         }
591       }
592       qw(
593         id gender abteilung title position givenname name email phone1 phone2 fax mobile1 mobile2
594         satphone satfax project street zipcode city privatphone privatemail birthday main
595       )
596     )
597   };
598
599   $data->{contact_cvars} = $self->_prepare_cvar_configs_for_ajaj($self->{contact}->cvars_by_config);
600
601   # avoid two or more main_cp
602   my $has_main_cp = grep { $_->cp_main == 1 } @{ $self->{cv}->contacts };
603   $data->{contact}->{disable_cp_main} = 1 if ($has_main_cp && !$data->{contact}->{cp_main});
604
605   $self->render(\SL::JSON::to_json($data), { type => 'json', process => 0 });
606 }
607
608 sub action_ajaj_autocomplete {
609   my ($self, %params) = @_;
610
611   my ($model, $manager, $number, $matches);
612
613   # first see if this is customer or vendor picking
614   if ($::form->{type} eq 'customer') {
615      $model   = $self->customer_models;
616      $manager = 'SL::DB::Manager::Customer';
617      $number  = 'customernumber';
618   } elsif ($::form->{type} eq 'vendor')  {
619      $model   = $self->vendor_models;
620      $manager = 'SL::DB::Manager::Vendor';
621      $number  = 'vendornumber';
622   } else {
623      die "unknown type $::form->{type}";
624   }
625
626   # if someone types something, and hits enter, assume he entered the full name.
627   # if something matches, treat that as the sole match
628   # unfortunately get_models can't do more than one per package atm, so we do it
629   # the oldfashioned way.
630   if ($::form->{prefer_exact}) {
631     my $exact_matches;
632     if (1 == scalar @{ $exact_matches = $manager->get_all(
633       query => [
634         obsolete => 0,
635         or => [
636           name    => { ilike => $::form->{filter}{'all:substr:multi::ilike'} },
637           $number => { ilike => $::form->{filter}{'all:substr:multi::ilike'} },
638         ]
639       ],
640       limit => 2,
641     ) }) {
642       $matches = $exact_matches;
643     }
644   }
645
646   $matches //= $model->get;
647
648   my @hashes = map {
649    +{
650      value       => $_->displayable_name,
651      label       => $_->displayable_name,
652      id          => $_->id,
653      $number     => $_->$number,
654      name        => $_->name,
655      type        => $::form->{type},
656      cvars       => { map { ($_->config->name => { value => $_->value_as_text, is_valid => $_->is_valid }) } @{ $_->cvars_by_config } },
657     }
658   } @{ $matches };
659
660   $self->render(\ SL::JSON::to_json(\@hashes), { layout => 0, type => 'json', process => 0 });
661 }
662
663 sub action_test_page {
664   $_[0]->render('customer_vendor/test_page');
665 }
666
667 sub action_ajax_list_prices {
668   my ($self, %params) = @_;
669
670   my $report   = SL::ReportGenerator->new(\%::myconfig, $::form);
671   my @columns  = qw(partnumber description price);
672   my @visible  = qw(partnumber description price);
673   my @sortable = qw(partnumber description price);
674
675   my %column_defs = (
676     partnumber  => { text => $::locale->text('Part Number'),      sub => sub { $_[0]->parts->partnumber  } },
677     description => { text => $::locale->text('Part Description'), sub => sub { $_[0]->parts->description } },
678     price       => { text => $::locale->text('Price'),            sub => sub { $::form->format_amount(\%::myconfig, $_[0]->price, 2) }, align => 'right' },
679   );
680
681   $::form->{sort_by}  ||= 'partnumber';
682   $::form->{sort_dir} //= 1;
683
684   for my $col (@sortable) {
685     $column_defs{$col}{link} = $self->url_for(
686       action   => 'ajax_list_prices',
687       callback => $::form->{callback},
688       db       => $::form->{db},
689       id       => $self->{cv}->id,
690       sort_by  => $col,
691       sort_dir => ($::form->{sort_by} eq $col ? 1 - $::form->{sort_dir} : $::form->{sort_dir})
692     );
693   }
694
695   map { $column_defs{$_}{visible} = 1 } @visible;
696
697   my $pricegroup;
698   $pricegroup = $self->{cv}->pricegroup->pricegroup if $self->{cv}->pricegroup;
699
700   $report->set_columns(%column_defs);
701   $report->set_column_order(@columns);
702   $report->set_options(allow_pdf_export => 0, allow_csv_export => 0);
703   $report->set_sort_indicator($::form->{sort_by}, $::form->{sort_dir});
704   $report->set_export_options(@{ $params{report_generator_export_options} || [] });
705   $report->set_options(
706     %{ $params{report_generator_options} || {} },
707     output_format        => 'HTML',
708     top_info_text        => $::locale->text('Pricegroup') . ': ' . $pricegroup,
709     title                => $::locale->text('Price List'),
710   );
711
712   my $sort_param = $::form->{sort_by} eq 'price'       ? 'price'             :
713                    $::form->{sort_by} eq 'description' ? 'parts.description' :
714                    'parts.partnumber';
715   $sort_param .= ' ' . ($::form->{sort_dir} ? 'ASC' : 'DESC');
716   my $prices = SL::DB::Manager::Price->get_all(where        => [ pricegroup_id => $self->{cv}->pricegroup_id ],
717                                                sort_by      => $sort_param,
718                                                with_objects => 'parts');
719
720   $self->report_generator_list_objects(report => $report, objects => $prices, layout => 0, header => 0);
721 }
722
723 sub is_vendor {
724   return $::form->{db} eq 'vendor';
725 }
726
727 sub is_customer {
728   return $::form->{db} eq 'customer';
729 }
730
731 sub is_orphaned {
732   my ($self) = @_;
733
734   if ( defined($self->{_is_orphaned}) ) {
735     return $self->{_is_orphaned};
736   }
737
738   my $arap      = $self->is_vendor ? 'ap' : 'ar';
739   my $num_args  = 3;
740
741   my $cv = $self->is_vendor ? 'vendor' : 'customer';
742
743   my $query =
744    'SELECT a.id
745     FROM '. $arap .' AS a
746     JOIN '. $cv .' ct ON (a.'. $cv .'_id = ct.id)
747     WHERE ct.id = ?
748
749     UNION
750
751     SELECT a.id
752     FROM oe a
753     JOIN '. $cv .' ct ON (a.'. $cv .'_id = ct.id)
754     WHERE ct.id = ?
755
756     UNION
757
758     SELECT a.id
759     FROM delivery_orders a
760     JOIN '. $cv .' ct ON (a.'. $cv .'_id = ct.id)
761     WHERE ct.id = ?';
762
763
764   if ( $self->is_vendor ) {
765     $query .=
766      ' UNION
767       SELECT 1 FROM makemodel mm WHERE mm.make = ?';
768     $num_args++;
769   }
770
771   my ($dummy) = selectrow_query($::form, $::form->get_standard_dbh(), $query, (conv_i($self->{cv}->id)) x $num_args);
772
773   return $self->{_is_orphaned} = !$dummy;
774 }
775
776 sub _copy_form_to_cvars {
777   my ($self, %params) = @_;
778
779   foreach my $cvar (@{ $params{target}->cvars_by_config }) {
780     my $value = $params{source}->{$cvar->config->name};
781     $value    = $::form->parse_amount(\%::myconfig, $value) if $cvar->config->type eq 'number';
782
783     $cvar->value($value);
784   }
785 }
786
787 sub _instantiate_args {
788   my ($self) = @_;
789
790   my $curr_employee = SL::DB::Manager::Employee->current;
791
792   if ( $::form->{cv}->{id} ) {
793     if ( $self->is_vendor() ) {
794       $self->{cv} = SL::DB::Vendor->new(id => $::form->{cv}->{id})->load();
795     } else {
796       $self->{cv} = SL::DB::Customer->new(id => $::form->{cv}->{id})->load();
797     }
798   } else {
799     $self->{cv} = $self->_new_customer_vendor_object;
800   }
801   $self->{cv}->assign_attributes(%{$::form->{cv}});
802
803   if ( $self->is_customer() && $::form->{cv}->{taxincluded_checked} eq '' ) {
804     $self->{cv}->taxincluded_checked(undef);
805   }
806
807   $self->{cv}->hourly_rate($::instance_conf->get_customer_hourly_rate) if $self->is_customer && !$self->{cv}->hourly_rate;
808
809   if ( $::form->{note}->{id} ) {
810     $self->{note} = SL::DB::Note->new(id => $::form->{note}->{id})->load();
811     $self->{note_followup} = $self->{note}->follow_up;
812     $self->{note_followup_link} = $self->{note_followup}->follow_up_link;
813   } else {
814     $self->{note} = SL::DB::Note->new();
815     $self->{note_followup} = SL::DB::FollowUp->new();
816     $self->{note_followup_link} = SL::DB::FollowUpLink->new();
817   }
818
819   $self->{note}->assign_attributes(%{$::form->{note}});
820   $self->{note}->created_by($curr_employee->id);
821   $self->{note}->trans_module('ct');
822
823   $self->{note_followup}->assign_attributes(%{$::form->{note_followup}});
824   $self->{note_followup}->note($self->{note});
825   $self->{note_followup}->created_by($curr_employee->id);
826
827   $self->{note_followup_link}->trans_type($self->is_vendor() ? 'vendor' : 'customer');
828   $self->{note_followup_link}->trans_info($self->{cv}->name);
829
830   if ( $::form->{shipto}->{shipto_id} ) {
831     $self->{shipto} = SL::DB::Shipto->new(shipto_id => $::form->{shipto}->{shipto_id})->load();
832   } else {
833     $self->{shipto} = SL::DB::Shipto->new();
834   }
835   $self->{shipto}->assign_attributes(%{$::form->{shipto}});
836   $self->{shipto}->module('CT');
837
838   if ( $::form->{contact}->{cp_id} ) {
839     $self->{contact} = SL::DB::Contact->new(cp_id => $::form->{contact}->{cp_id})->load();
840   } else {
841     $self->{contact} = $self->_new_contact_object;
842   }
843   $self->{contact}->assign_attributes(%{$::form->{contact}});
844
845   $self->_copy_form_to_cvars(target => $self->{cv},      source => $::form->{cv_cvars});
846   $self->_copy_form_to_cvars(target => $self->{contact}, source => $::form->{contact_cvars});
847   $self->_copy_form_to_cvars(target => $self->{shipto},  source => $::form->{shipto_cvars});
848 }
849
850 sub _load_customer_vendor {
851   my ($self) = @_;
852
853   if ( $self->is_vendor() ) {
854     $self->{cv} = SL::DB::Vendor->new(id => $::form->{id})->load();
855   } else {
856     $self->{cv} = SL::DB::Customer->new(id => $::form->{id})->load();
857   }
858
859   if ( $::form->{note_id} ) {
860     $self->{note} = SL::DB::Note->new(id => $::form->{note_id})->load();
861     $self->{note_followup} = $self->{note}->follow_up;
862     $self->{note_followup_link} = $self->{note_followup}->follow_up_link;
863   } else {
864     $self->{note} = SL::DB::Note->new();
865     $self->{note_followup} = SL::DB::FollowUp->new();
866     $self->{note_followup_link} = SL::DB::FollowUpLink->new();
867   }
868
869   if ( $::form->{shipto_id} ) {
870     $self->{shipto} = SL::DB::Shipto->new(shipto_id => $::form->{shipto_id})->load();
871
872     if ( $self->{shipto}->trans_id != $self->{cv}->id ) {
873       die($::locale->text('Error'));
874     }
875   } else {
876     $self->{shipto} = SL::DB::Shipto->new();
877   }
878
879   if ( $::form->{contact_id} ) {
880     $self->{contact} = SL::DB::Contact->new(cp_id => $::form->{contact_id})->load();
881
882     if ( $self->{contact}->cp_cv_id != $self->{cv}->id ) {
883       die($::locale->text('Error'));
884     }
885   } else {
886     $self->{contact} = $self->_new_contact_object;
887   }
888 }
889
890 sub _may_access_action {
891   my ($self, $action)   = @_;
892
893   my $is_new            = !$self->{cv} || !$self->{cv}->id;
894   my $is_own_customer   = !$is_new
895                        && $self->{cv}->is_customer
896                        && (SL::DB::Manager::Employee->current->id == $self->{cv}->salesman_id);
897   my $has_edit_rights   = $::auth->assert('customer_vendor_all_edit', 1);
898   $has_edit_rights    ||= $::auth->assert('customer_vendor_edit',     1) && ($is_new || $is_own_customer);
899   my $needs_edit_rights = $action =~ m{^(?:add|save|delete|update)};
900
901   $self->user_has_edit_rights($has_edit_rights);
902
903   return 1 if $has_edit_rights;
904   return 0 if $needs_edit_rights;
905   return 1;
906 }
907
908 sub _check_auth {
909   my ($self, $action) = @_;
910
911   if (!$self->_may_access_action($action)) {
912     $::auth->deny_access;
913   }
914 }
915
916 sub _create_customer_vendor {
917   my ($self) = @_;
918
919   $self->{cv} = $self->_new_customer_vendor_object;
920   $self->{cv}->currency_id($::instance_conf->get_currency_id());
921
922   $self->{note} = SL::DB::Note->new();
923
924   $self->{note_followup} = SL::DB::FollowUp->new();
925
926   $self->{shipto} = SL::DB::Shipto->new();
927
928   $self->{contact} = $self->_new_contact_object;
929 }
930
931 sub _pre_render {
932   my ($self) = @_;
933
934   my $dbh = $::form->get_standard_dbh();
935
936   my $query;
937
938   $self->{all_business} = SL::DB::Manager::Business->get_all();
939
940   $self->{all_employees} = SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
941
942   $self->{all_greetings} = SL::DB::Manager::Greeting->get_all_sorted();
943   if ($self->{cv}->id && $self->{cv}->greeting && !grep {$self->{cv}->greeting eq $_->description} @{$self->{all_greetings}}) {
944     unshift @{$self->{all_greetings}}, (SL::DB::Greeting->new(description => $self->{cv}->greeting));
945   }
946
947   $self->{all_contact_titles} = SL::DB::Manager::ContactTitle->get_all_sorted();
948   foreach my $contact (@{ $self->{cv}->contacts }) {
949     if ($contact->cp_title && !grep {$contact->cp_title eq $_->description} @{$self->{all_contact_titles}}) {
950       unshift @{$self->{all_contact_titles}}, (SL::DB::ContactTitle->new(description => $contact->cp_title));
951     }
952   }
953
954   $self->{all_contact_departments} = SL::DB::Manager::ContactDepartment->get_all_sorted();
955   foreach my $contact (@{ $self->{cv}->contacts }) {
956     if ($contact->cp_abteilung && !grep {$contact->cp_abteilung eq $_->description} @{$self->{all_contact_departments}}) {
957       unshift @{$self->{all_contact_departments}}, (SL::DB::ContactDepartment->new(description => $contact->cp_abteilung));
958     }
959   }
960
961   $self->{all_currencies} = SL::DB::Manager::Currency->get_all();
962
963   $self->{all_languages} = SL::DB::Manager::Language->get_all();
964
965   $self->{all_taxzones} = SL::DB::Manager::TaxZone->get_all_sorted();
966
967   if ( $::instance_conf->get_vertreter() ) {
968     $query =
969       'SELECT id
970        FROM business
971        WHERE salesman';
972     my $business_ids = [
973       map(
974         { $_->{id}; }
975         selectall_hashref_query($::form, $dbh, $query)
976       )
977     ];
978
979     if ( $business_ids->[0] ) {
980       $self->{all_salesman_customers} = SL::DB::Manager::Customer->get_all(query => [business_id => $business_ids]);
981     } else {
982       $self->{all_salesman_customers} = [];
983     }
984   } else {
985     $self->{all_salesmen} = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $self->{cv}->salesman_id,  deleted => 0 ] ]);
986   }
987
988   $self->{all_payment_terms} = SL::DB::Manager::PaymentTerm->get_all_sorted(where => [ or => [ id       => $self->{cv}->payment_id,
989                                                                                                obsolete => 0 ] ]);
990
991   $self->{all_delivery_terms} = SL::DB::Manager::DeliveryTerm->get_all();
992
993   if ($self->{cv}->is_customer) {
994     $self->{all_pricegroups} = SL::DB::Manager::Pricegroup->get_all_sorted(query => [ or => [ id => $self->{cv}->pricegroup_id, obsolete => 0 ] ]);
995   }
996
997   $self->{contacts} = $self->{cv}->contacts;
998   $self->{contacts} ||= [];
999
1000   $self->{shiptos} = $self->{cv}->shipto;
1001   $self->{shiptos} ||= [];
1002
1003   $self->{notes} = SL::DB::Manager::Note->get_all(
1004     query => [
1005       trans_id => $self->{cv}->id,
1006       trans_module => 'ct',
1007     ],
1008     with_objects => ['follow_up'],
1009   );
1010
1011   if ( $self->is_vendor()) {
1012     $self->{open_items} = SL::DB::Manager::PurchaseInvoice->get_all_count(
1013       query => [
1014         vendor_id => $self->{cv}->id,
1015         paid => {lt_sql => 'amount'},
1016       ],
1017     );
1018   } else {
1019     $self->{open_items} = SL::DB::Manager::Invoice->get_all_count(
1020       query => [
1021         customer_id => $self->{cv}->id,
1022         paid => {lt_sql => 'amount'},
1023       ],
1024     );
1025   }
1026
1027   if ( $self->is_vendor() ) {
1028     $self->{open_orders} = SL::DB::Manager::Order->get_all_count(
1029       query => [
1030         vendor_id => $self->{cv}->id,
1031         closed => 'F',
1032       ],
1033     );
1034   } else {
1035     $self->{open_orders} = SL::DB::Manager::Order->get_all_count(
1036       query => [
1037         customer_id => $self->{cv}->id,
1038         closed => 'F',
1039       ],
1040     );
1041   }
1042
1043   if ($self->{cv}->number && $::instance_conf->get_webdav) {
1044     my $webdav = SL::Webdav->new(
1045       type     => $self->is_customer ? 'customer'
1046                 : $self->is_vendor   ? 'vendor'
1047                 : undef,
1048       number   => $self->{cv}->number,
1049     );
1050     my @all_objects = $webdav->get_all_objects;
1051     @{ $self->{template_args}->{WEBDAV} } = map { { name => $_->filename,
1052                                                     type => t8('File'),
1053                                                     link => File::Spec->catfile($_->full_filedescriptor),
1054                                                 } } @all_objects;
1055   }
1056
1057   $self->{template_args} ||= {};
1058
1059   $::request->{layout}->add_javascripts('kivi.CustomerVendor.js');
1060   $::request->{layout}->add_javascripts('kivi.File.js');
1061   $::request->{layout}->add_javascripts('kivi.CustomerVendorTurnover.js');
1062
1063   $self->_setup_form_action_bar;
1064 }
1065
1066 sub _setup_form_action_bar {
1067   my ($self) = @_;
1068
1069   my $no_rights = $self->user_has_edit_rights ? undef
1070                 : $self->{cv}->is_customer    ? t8("You don't have the rights to edit this customer.")
1071                 :                               t8("You don't have the rights to edit this vendor.");
1072
1073   for my $bar ($::request->layout->get('actionbar')) {
1074     $bar->add(
1075       combobox => [
1076         action => [
1077           t8('Save'),
1078           submit    => [ '#form', { action => "CustomerVendor/save" } ],
1079           checks    => [ 'check_taxzone_and_ustid' ],
1080           accesskey => 'enter',
1081           disabled  => $no_rights,
1082         ],
1083         action => [
1084           t8('Save and Close'),
1085           submit => [ '#form', { action => "CustomerVendor/save_and_close" } ],
1086           checks => [ 'check_taxzone_and_ustid' ],
1087           disabled => $no_rights,
1088         ],
1089       ], # end of combobox "Save"
1090
1091       combobox => [
1092         action => [ t8('Workflow') ],
1093         (action => [
1094           t8('Save and AP Transaction'),
1095           submit => [ '#form', { action => "CustomerVendor/save_and_ap_transaction" } ],
1096           checks => [ 'check_taxzone_and_ustid' ],
1097           disabled => $no_rights,
1098         ]) x !!$self->is_vendor,
1099         (action => [
1100           t8('Save and AR Transaction'),
1101           submit => [ '#form', { action => "CustomerVendor/save_and_ar_transaction" } ],
1102           checks => [ 'check_taxzone_and_ustid' ],
1103           disabled => $no_rights,
1104         ]) x !$self->is_vendor,
1105         action => [
1106           t8('Save and Invoice'),
1107           submit => [ '#form', { action => "CustomerVendor/save_and_invoice" } ],
1108           checks => [ 'check_taxzone_and_ustid' ],
1109           disabled => $no_rights,
1110         ],
1111         action => [
1112           t8('Save and Order'),
1113           submit => [ '#form', { action => "CustomerVendor/save_and_order" } ],
1114           checks => [ 'check_taxzone_and_ustid' ],
1115           disabled => $no_rights,
1116         ],
1117         (action => [
1118           t8('Save and RFQ'),
1119           submit => [ '#form', { action => "CustomerVendor/save_and_rfq" } ],
1120           checks => [ 'check_taxzone_and_ustid' ],
1121           disabled => $no_rights,
1122         ]) x !!$self->is_vendor,
1123         (action => [
1124           t8('Save and Quotation'),
1125           submit => [ '#form', { action => "CustomerVendor/save_and_quotation" } ],
1126           checks => [ 'check_taxzone_and_ustid' ],
1127           disabled => $no_rights,
1128         ]) x !$self->is_vendor,
1129       ], # end of combobox "Workflow"
1130
1131       action => [
1132         t8('Delete'),
1133         submit   => [ '#form', { action => "CustomerVendor/delete" } ],
1134         confirm  => t8('Do you really want to delete this object?'),
1135         disabled => !$self->{cv}->id    ? t8('This object has not been saved yet.')
1136                   : !$self->is_orphaned ? t8('This object has already been used.')
1137                   :                       $no_rights,
1138       ],
1139
1140       'separator',
1141
1142       action => [
1143         t8('History'),
1144         call     => [ 'kivi.CustomerVendor.showHistoryWindow', $self->{cv}->id ],
1145         disabled => !$self->{cv}->id ? t8('This object has not been saved yet.') : undef,
1146       ],
1147     );
1148   }
1149 }
1150
1151 sub _prepare_cvar_configs_for_ajaj {
1152   my ($self, $cvars) = @_;
1153
1154   return {
1155     map {
1156       my $cvar   = $_;
1157       my $result = { type => $cvar->config->type };
1158
1159       if ($cvar->config->type eq 'number') {
1160         $result->{value} = $::form->format_amount(\%::myconfig, $cvar->value, -2);
1161
1162       } elsif ($result->{type} eq 'date') {
1163         $result->{value} = $cvar->value ? $cvar->value->to_kivitendo : undef;
1164
1165       } elsif ($result->{type} =~ m{customer|vendor|part}) {
1166         my $object       = $cvar->value;
1167         my $method       = $result->{type} eq 'part' ? 'description' : 'name';
1168
1169         $result->{id}    = int($cvar->number_value) || undef;
1170         $result->{value} = $object ? $object->$method // '' : '';
1171
1172       } else {
1173         $result->{value} = $cvar->value;
1174       }
1175
1176       ( $cvar->config->name => $result )
1177
1178     } grep { $_->is_valid } @{ $cvars }
1179   };
1180 }
1181
1182 sub normalize_name {
1183   my ($self) = @_;
1184
1185   # check if feature is enabled (select normalize_vc_names from defaults)
1186   return unless ($::instance_conf->get_normalize_vc_names);
1187
1188   return unless $self->{cv};
1189   my $name = $self->{cv}->name;
1190   $name =~ s/\s+$//;
1191   $name =~ s/^\s+//;
1192   $name =~ s/\s+/ /g;
1193   $self->{cv}->name($name);
1194 }
1195
1196 sub home_address_for_google_maps {
1197   my ($self)  = @_;
1198
1199   my $address = $::instance_conf->get_address // '';
1200   $address    =~ s{^\s+|\s+$|\r+}{}g;
1201   $address    =~ s{\n+}{,}g;
1202   $address    =~ s{\s+}{ }g;
1203
1204   return $address;
1205 }
1206
1207 sub init_customer_models {
1208   my ($self) = @_;
1209
1210   SL::Controller::Helper::GetModels->new(
1211     controller   => $self,
1212     model        => 'Customer',
1213     sorted => {
1214       _default  => {
1215         by => 'customernumber',
1216         dir  => 1,
1217       },
1218       customernumber => t8('Customer Number'),
1219     },
1220   );
1221 }
1222
1223 sub init_vendor_models {
1224   my ($self) = @_;
1225
1226   SL::Controller::Helper::GetModels->new(
1227     controller => $self,
1228     model      => 'Vendor',
1229     sorted => {
1230       _default  => {
1231         by => 'vendornumber',
1232         dir  => 1,
1233       },
1234       vendornumber => t8('Vendor Number'),
1235     },
1236   );
1237 }
1238
1239 sub init_zugferd_settings {
1240   return [
1241     [ -1, t8('Use settings from client configuration') ],
1242     @SL::ZUGFeRD::customer_settings,
1243   ],
1244 }
1245
1246 sub _new_customer_vendor_object {
1247   my ($self) = @_;
1248
1249   my $class  = 'SL::DB::' . ($self->is_vendor ? 'Vendor' : 'Customer');
1250   return $class->new(
1251     contacts         => [],
1252     shipto           => [],
1253     custom_variables => [],
1254   );
1255 }
1256
1257 sub _new_contact_object {
1258   my ($self) = @_;
1259
1260   return SL::DB::Contact->new(custom_variables => []);
1261 }
1262
1263 1;