Cuddled else
[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 SL::JSON;
7 use SL::DBUtils;
8 use SL::Helper::Flash;
9
10 use SL::DB::Customer;
11 use SL::DB::Vendor;
12 use SL::DB::Business;
13 use SL::DB::Employee;
14 use SL::DB::Language;
15 use SL::DB::TaxZone;
16 use SL::DB::Note;
17 use SL::DB::PaymentTerm;
18 use SL::DB::Pricegroup;
19 use SL::DB::Contact;
20 use SL::DB::FollowUp;
21 use SL::DB::History;
22 use SL::DB::Currency;
23
24 # safety
25 __PACKAGE__->run_before(
26   sub {
27     $::auth->assert('customer_vendor_edit');
28   }
29 );
30
31 __PACKAGE__->run_before(
32   '_instantiate_args',
33   only => [
34     'save',
35     'save_and_ap_transaction',
36     'save_and_ar_transaction',
37     'save_and_close',
38     'save_and_invoice',
39     'save_and_order',
40     'save_and_quotation',
41     'save_and_rfq',
42     'delete_contact',
43     'delete_shipto',
44   ]
45 );
46
47 __PACKAGE__->run_before(
48   '_load_customer_vendor',
49   only => [
50     'edit',
51     'update',
52     'ajaj_get_shipto',
53     'ajaj_get_contact',
54   ]
55 );
56 __PACKAGE__->run_before(
57   '_create_customer_vendor',
58   only => [
59     'add',
60   ]
61 );
62
63 sub action_add {
64   my ($self) = @_;
65
66   $self->_pre_render();
67   $self->render(
68     'customer_vendor/form',
69     title => ($self->is_vendor() ? $::locale->text('Add Vendor') : $::locale->text('Add Customer')),
70     %{$self->{template_args}}
71   );
72 }
73
74 sub action_edit {
75   my ($self) = @_;
76
77   $self->_pre_render();
78   $self->render(
79     'customer_vendor/form',
80     title => ($self->is_vendor() ? $::locale->text('Edit Vendor') : $::locale->text('Edit Customer')),
81     %{$self->{template_args}}
82   );
83 }
84
85 sub _save {
86   my ($self) = @_;
87
88   my $db = $self->{cv}->db;
89
90   $db->do_transaction(sub {
91     my $cvs_by_nr;
92     if ( $self->is_vendor() ) {
93       if ( $self->{cv}->vendornumber ) {
94         $cvs_by_nr = SL::DB::Manager::Vendor->get_all(query => [vendornumber => $self->{cv}->vendornumber]);
95       }
96     } else {
97       if ( $self->{cv}->customernumber ) {
98         $cvs_by_nr = SL::DB::Manager::Customer->get_all(query => [customernumber => $self->{cv}->customernumber]);
99       }
100     }
101
102     foreach my $entry (@{$cvs_by_nr}) {
103       if( $entry->id != $self->{cv}->id ) {
104         my $msg =
105           $self->is_vendor() ? $::locale->text('This vendor number is already in use.') : $::locale->text('This customer number is already in use.');
106
107         $::form->error($msg);
108       }
109     }
110
111     $self->{cv}->save(cascade => 1);
112
113     $self->{contact}->cp_cv_id($self->{cv}->id);
114     if( $self->{contact}->cp_name ne '' || $self->{contact}->cp_givenname ne '' ) {
115       $self->{contact}->save();
116     }
117
118     if( $self->{note}->subject ne '' && $self->{note}->body ne '' ) {
119       $self->{note}->trans_id($self->{cv}->id);
120       $self->{note}->save();
121       $self->{note_followup}->save();
122
123       $self->{note} = SL::DB::Note->new();
124       $self->{note_followup} = SL::DB::FollowUp->new();
125     }
126
127     $self->{shipto}->trans_id($self->{cv}->id);
128     if( $self->{shipto}->shiptoname ne '' ) {
129       $self->{shipto}->save();
130     }
131
132     my $snumbers = $self->is_vendor() ? 'vendornumber_'. $self->{cv}->vendornumber : 'customernumber_'. $self->{cv}->customernumber;
133     SL::DB::History->new(
134       trans_id => $self->{cv}->id,
135       snumbers => $snumbers,
136       employee_id => SL::DB::Manager::Employee->current->id,
137       addition => 'SAVED',
138     )->save();
139   }) || die($db->error);
140
141 }
142
143 sub action_save {
144   my ($self) = @_;
145
146   $self->_save();
147
148   $self->redirect_to(action => 'edit', id => $self->{cv}->id);
149 }
150
151 sub action_save_and_close {
152   my ($self) = @_;
153
154   $self->_save();
155
156   my $msg = $self->is_vendor() ? $::locale->text('Vendor saved') : $::locale->text('Customer saved');
157   $::form->redirect($msg);
158 }
159
160 sub _transaction {
161   my ($self, $script) = @_;
162
163   $::auth->assert('general_ledger         | invoice_edit         | vendor_invoice_edit | ' .
164                  ' request_quotation_edit | sales_quotation_edit | sales_order_edit    | purchase_order_edit');
165
166   $self->_save();
167
168   my $callback = $::form->escape($::form->{callback}, 1);
169   my $name = $::form->escape($self->{cv}->name, 1);
170   my $db = $self->is_vendor() ? 'vendor' : 'customer';
171
172   my $url =
173     $script .'?'.
174     'action=add&'.
175     'vc='. $db .'&'.
176     $db .'_id=' . $self->{cv}->id .'&'.
177     $db .'='. $name .'&'.
178     'type='. $::form->{type} .'&'.
179     'callback='. $callback;
180
181   print $::form->redirect_header($url);
182 }
183
184 sub action_save_and_ar_transaction {
185   my ($self) = @_;
186
187   $main::auth->assert('general_ledger');
188
189   $self->_transaction('ar.pl');
190 }
191
192 sub action_save_and_ap_transaction {
193   my ($self) = @_;
194
195   $main::auth->assert('general_ledger');
196
197   $self->_transaction('ap.pl');
198 }
199
200 sub action_save_and_invoice {
201   my ($self) = @_;
202
203   if ( $self->is_vendor() ) {
204     $::auth->assert('vendor_invoice_edit');
205   } else {
206     $::auth->assert('invoice_edit');
207   }
208
209   $::form->{type} = 'invoice';
210   $self->_transaction($self->is_vendor() ? 'ir.pl' : 'is.pl');
211 }
212
213 sub action_save_and_order {
214   my ($self) = @_;
215
216   if ( $self->is_vendor() ) {
217     $::auth->assert('purchase_order_edit');
218   } else {
219     $::auth->assert('sales_order_edit');
220   }
221
222   $::form->{type} = $self->is_vendor() ? 'purchase_order' : 'sales_order';
223   $self->_transaction('oe.pl');
224 }
225
226 sub action_save_and_rfq {
227   my ($self) = @_;
228
229   $::auth->assert('request_quotation_edit');
230
231   $::form->{type} = 'request_quotation';
232   $self->_transaction('oe.pl');
233 }
234
235 sub action_save_and_quotation {
236   my ($self) = @_;
237
238   $::auth->assert('sales_quotation_edit');
239
240   $::form->{type} = 'sales_quotation';
241   $self->_transaction('oe.pl');
242 }
243
244 sub action_delete {
245   my ($self) = @_;
246
247   my $db = $self->{cv}->db;
248
249   if( !$self->is_orphaned() ) {
250     $self->action_edit();
251   } else {
252
253     $db->do_transaction(sub {
254       $self->{cv}->delete(cascade => 1);
255
256       my $snumbers = $self->is_vendor() ? 'vendornumber_'. $self->{cv}->vendornumber : 'customernumber_'. $self->{cv}->customernumber;
257       SL::DB::History->new(
258         trans_id => $self->{cv}->id,
259         snumbers => $snumbers,
260         employee_id => SL::DB::Manager::Employee->current->id,
261         addition => 'DELETED',
262       )->save();
263     }) || die($db->error);
264
265     my $msg = $self->is_vendor() ? $::locale->text('Vendor deleted!') : $::locale->text('Customer deleted!');
266     $::form->redirect($msg);
267   }
268
269 }
270
271
272 sub action_delete_contact {
273   my ($self) = @_;
274
275   my $db = $self->{contact}->db;
276
277   if ( !$self->{contact}->cp_id ) {
278     SL::Helper::Flash::flash('error', $::locale->text('No contact selected to delete'));
279   } else {
280
281     $db->do_transaction(sub {
282       if ( $self->{contact}->used ) {
283         $self->{contact}->detach();
284         $self->{contact}->save();
285         SL::Helper::Flash::flash('info', $::locale->text('Contact is in use and was flagged invalid.'));
286       } else {
287         $self->{contact}->delete(cascade => 1);
288         SL::Helper::Flash::flash('info', $::locale->text('Contact deleted.'));
289       }
290     }) || die($db->error);
291
292     $self->{contact} = SL::DB::Contact->new();
293   }
294
295   $self->action_edit();
296 }
297
298 sub action_delete_shipto {
299   my ($self) = @_;
300
301   my $db = $self->{shipto}->db;
302
303   if ( !$self->{shipto}->shipto_id ) {
304     SL::Helper::Flash::flash('error', $::locale->text('No shipto selected to delete'));
305   } else {
306
307     $db->do_transaction(sub {
308       if ( $self->{shipto}->used ) {
309         $self->{shipto}->detach();
310         $self->{shipto}->save(cascade => 1);
311         SL::Helper::Flash::flash('info', $::locale->text('Shipto is in use and was flagged invalid.'));
312       } else {
313         $self->{shipto}->delete(cascade => 1);
314         SL::Helper::Flash::flash('info', $::locale->text('Shipto deleted.'));
315       }
316     }) || die($db->error);
317
318     $self->{shipto} = SL::DB::Shipto->new();
319   }
320
321   $self->action_edit();
322 }
323
324
325 sub action_search {
326   my ($self) = @_;
327
328   my $url = 'ct.pl?action=search&db='. ($self->is_vendor() ? 'vendor' : 'customer');
329
330   if ( $::form->{callback} ) {
331     $url .= '&callback='. $::from->escape($::form->{callback});
332   }
333
334   print $::form->redirect_header($url);
335 }
336
337
338 sub action_search_contact {
339   my ($self) = @_;
340
341   my $url = 'ct.pl?action=search_contact&db=customer';
342
343   if ( $::form->{callback} ) {
344     $url .= '&callback='. $::from->escape($::form->{callback});
345   }
346
347   print $::form->redirect_header($url);
348 }
349
350
351 sub action_get_delivery {
352   my ($self) = @_;
353
354   my $dbh = $::form->get_standard_dbh();
355
356   my ($arap, $db, $qty_sign);
357   if ( $self->is_vendor() ) {
358     $arap = 'ap';
359     $db = 'vendor';
360     $qty_sign = ' * -1 AS qty';
361   } else {
362     $arap = 'ar';
363     $db = 'customer';
364     $qty_sign = '';
365   }
366
367   my $where = ' WHERE 1=1 ';
368   my @values;
369
370   if ( !$self->is_vendor() && $::form->{shipto_id} && $::form->{shipto_id} ne 'all' ) {
371     $where .= "AND ${arap}.shipto_id = ?";
372     push(@values, $::form->{shipto_id});
373   }
374
375   if ( $::form->{delivery_from} ) {
376     $where .= "AND ${arap}.transdate >= ?";
377     push(@values, conv_date($::form->{delivery_from}));
378   }
379
380   if ( $::form->{delivery_to} ) {
381     $where .= "AND ${arap}.transdate <= ?";
382     push(@values, conv_date($::form->{delivery_to}));
383   }
384
385   my $query =
386     "SELECT
387        s.shiptoname,
388        i.qty ${qty_sign},
389        ${arap}.id,
390        ${arap}.transdate,
391        ${arap}.invnumber,
392        ${arap}.ordnumber,
393        i.description,
394        i.unit,
395        i.sellprice,
396        oe.id AS oe_id,
397        invoice
398      FROM ${arap}
399
400      LEFT JOIN shipto s
401       ON ". ($arap eq 'ar' ? '(ar.shipto_id = s.shipto_id) ' : '(ap.id = s.trans_id) ') ."
402
403      LEFT JOIN invoice i
404        ON ${arap}.id = i.trans_id
405
406      LEFT JOIN parts p
407        ON p.id = i.parts_id
408
409      LEFT JOIN oe
410        ON (oe.ordnumber = ${arap}.ordnumber AND NOT ${arap}.ordnumber = '')
411
412      ${where}
413      ORDER BY ${arap}.transdate DESC LIMIT 15";
414
415   $self->{delivery} = selectall_hashref_query($::form, $dbh, $query, @values);
416
417   $self->render('customer_vendor/get_delivery', { layout => 0 });
418 }
419
420 sub action_ajaj_get_shipto {
421   my ($self) = @_;
422
423   my $data = {
424     map(
425       {
426         my $name = 'shipto'. $_;
427         $name => $self->{shipto}->$name;
428       }
429       qw(_id name department_1 department_2 street zipcode city country contact phone fax email)
430     )
431   };
432
433   $self->render(\SL::JSON::to_json($data), { type => 'json', process => 0 });
434 }
435
436 sub action_ajaj_get_contact {
437   my ($self) = @_;
438
439   my $data;
440
441   $data->{contact} = {
442     map(
443       {
444         my $name = 'cp_'. $_;
445
446         if ( $_ eq 'birthday' && $self->{contact}->$name ) {
447           $name => $self->{contact}->$name->to_lxoffice;
448         } else {
449           $name => $self->{contact}->$name;
450         }
451       }
452       qw(
453         id gender abteilung title position givenname name email phone1 phone2 fax mobile1 mobile2
454         satphone satfax project street zipcode city privatphone privatemail birthday
455       )
456     )
457   };
458
459   $data->{contact_cvars} = {
460     map(
461       {
462         if ( $_->config->type eq 'number' ) {
463           $_->config->name => $::form->format_amount(\%::myconfig, $_->value, -2);
464         } else {
465           $_->config->name => $_->value;
466         }
467       }
468       grep(
469         { $_->is_valid; }
470         @{$self->{contact}->cvars_by_config}
471       )
472     )
473   };
474
475   $self->render(\SL::JSON::to_json($data), { type => 'json', process => 0 });
476 }
477
478 sub action_ajaj_customer_autocomplete {
479   my ($self, %params) = @_;
480
481   my $limit = $::form->{limit} || 20;
482   my $type  = $::form->{type}  || {};
483   my $query = { ilike => '%'. $::form->{term} .'%' };
484
485   my @filter;
486   push(
487     @filter,
488     $::form->{column} ? ($::form->{column} => $query) : (or => [ customernumber => $query, name => $query ])
489   );
490
491   my $customers = SL::DB::Manager::Customer->get_all(query => [ @filter ], limit => $limit);
492   my $value_col = $::form->{column} || 'name';
493
494   my $data = [
495     map(
496       {
497         {
498           value => $_->can($value_col)->($_),
499           label => $_->displayable_name,
500           id    => $_->id,
501           customernumber => $_->customernumber,
502           name  => $_->name,
503         }
504       }
505       @{$customers}
506     )
507   ];
508
509   $self->render(\SL::JSON::to_json($data), { layout => 0, type => 'json' });
510 }
511
512 sub is_vendor {
513   return $::form->{db} eq 'vendor';
514 }
515
516 sub is_customer {
517   return $::form->{db} eq 'customer';
518 }
519
520 sub is_orphaned {
521   my ($self) = @_;
522
523   if ( defined($self->{_is_orphaned}) ) {
524     return $self->{_is_orphaned};
525   }
526
527   my $arap      = $self->is_vendor ? 'ap' : 'ar';
528   my $num_args  = 2;
529
530   my $cv = $self->is_vendor ? 'vendor' : 'customer';
531
532   my $query =
533    'SELECT a.id
534     FROM '. $arap .' AS a
535     JOIN '. $cv .' ct ON (a.'. $cv .'_id = ct.id)
536     WHERE ct.id = ?
537
538     UNION
539
540     SELECT a.id
541     FROM oe a
542     JOIN '. $cv .' ct ON (a.'. $cv .'_id = ct.id)
543     WHERE ct.id = ?';
544
545
546   if ( $self->is_vendor ) {
547     $query .=
548      ' UNION
549       SELECT 1 FROM makemodel mm WHERE mm.make = ?';
550     $num_args++;
551   }
552
553   my ($dummy) = selectrow_query($::form, $::form->get_standard_dbh(), $query, (conv_i($self->{cv}->id)) x $num_args);
554
555   return $self->{_is_orphaned} = !$dummy;
556 }
557
558 sub _instantiate_args {
559   my ($self) = @_;
560
561   my $curr_employee = SL::DB::Manager::Employee->current;
562
563   if ( $::form->{cv}->{id} ) {
564     if ( $self->is_vendor() ) {
565       $self->{cv} = SL::DB::Vendor->new(id => $::form->{cv}->{id})->load();
566     } else {
567       $self->{cv} = SL::DB::Customer->new(id => $::form->{cv}->{id})->load();
568     }
569   } else {
570     if ( $self->is_vendor() ) {
571       $self->{cv} = SL::DB::Vendor->new();
572     } else {
573       $self->{cv} = SL::DB::Customer->new();
574     }
575   }
576   $self->{cv}->assign_attributes(%{$::form->{cv}});
577
578   foreach my $cvar (@{$self->{cv}->cvars_by_config()}) {
579     my $value = $::form->{cv_cvars}->{$cvar->config->name};
580
581     if ( $cvar->config->type eq 'number' ) {
582       $value = $::form->parse_amount(\%::myconfig, $value);
583     }
584
585     $cvar->value($value);
586   }
587
588 #  foreach my $cvar_key (keys(%{$::form->{cv_cvars}})) {
589 #    my $cvar_value = $::form->{cv_cvars}->{$cvar_key};
590 #    my $cvar = $self->{cv}->cvar_by_name($cvar_key);
591 #    $cvar->value($cvar_value);
592 #  }
593
594   if ( $::form->{note}->{id} ) {
595     $self->{note} = SL::DB::Note->new(id => $::form->{note}->{id})->load();
596   } else {
597     $self->{note} = SL::DB::Note->new();
598   }
599   $self->{note}->assign_attributes(%{$::form->{note}});
600   $self->{note}->created_by($curr_employee->id);
601   $self->{note}->trans_module('ct');
602   #  if ( $self->{note}->trans_id != $self->{cv}->id ) {
603   #    die($::locale->text('Error'));
604   #  }
605
606
607   $self->{note_followup} = SL::DB::FollowUp->new();
608   $self->{note_followup}->assign_attributes(%{$::form->{note_followup}});
609   $self->{note_followup}->note($self->{note});
610   $self->{note_followup}->created_by($curr_employee->id);
611
612   if ( $::form->{shipto}->{shipto_id} ) {
613     $self->{shipto} = SL::DB::Shipto->new(shipto_id => $::form->{shipto}->{shipto_id})->load();
614   } else {
615     $self->{shipto} = SL::DB::Shipto->new();
616   }
617   $self->{shipto}->assign_attributes(%{$::form->{shipto}});
618   $self->{shipto}->module('CT');
619 #  if ( $self->{shipto}->trans_id != $self->{cv}->id ) {
620 #    die($::locale->text('Error'));
621 #  }
622
623   if ( $::form->{contact}->{cp_id} ) {
624     $self->{contact} = SL::DB::Contact->new(cp_id => $::form->{contact}->{cp_id})->load();
625   } else {
626     $self->{contact} = SL::DB::Contact->new();
627   }
628   $self->{contact}->assign_attributes(%{$::form->{contact}});
629 #  if ( $self->{contact}->cp_cv_id != $self->{cv}->id ) {
630 #    die($::locale->text('Error'));
631 #  }
632
633   foreach my $cvar (@{$self->{contact}->cvars_by_config()}) {
634     my $value = $::form->{contact_cvars}->{$cvar->config->name};
635
636     if ( $cvar->config->type eq 'number' ) {
637       $value = $::form->parse_amount(\%::myconfig, $value);
638     }
639
640     $cvar->value($value);
641   }
642 }
643
644 sub _load_customer_vendor {
645   my ($self) = @_;
646
647   if ( $self->is_vendor() ) {
648     $self->{cv} = SL::DB::Vendor->new(id => $::form->{id})->load();
649   } else {
650     $self->{cv} = SL::DB::Customer->new(id => $::form->{id})->load();
651   }
652
653   $self->{note} = SL::DB::Note->new();
654   $self->{note_followup} = SL::DB::FollowUp->new();
655
656   if ( $::form->{shipto_id} ) {
657     $self->{shipto} = SL::DB::Shipto->new(shipto_id => $::form->{shipto_id})->load();
658
659     if ( $self->{shipto}->trans_id != $self->{cv}->id ) {
660       die($::locale->text('Error'));
661     }
662   } else {
663     $self->{shipto} = SL::DB::Shipto->new();
664   }
665
666   if ( $::form->{contact_id} ) {
667     $self->{contact} = SL::DB::Contact->new(cp_id => $::form->{contact_id})->load();
668
669     if ( $self->{contact}->cp_cv_id != $self->{cv}->id ) {
670       die($::locale->text('Error'));
671     }
672   } else {
673     $self->{contact} = SL::DB::Contact->new();
674   }
675 }
676
677 sub _create_customer_vendor {
678   my ($self) = @_;
679
680   if ( $self->is_vendor() ) {
681     $self->{cv} = SL::DB::Vendor->new();
682   } else {
683     $self->{cv} = SL::DB::Customer->new();
684   }
685
686   $self->{note} = SL::DB::Note->new();
687
688   $self->{note_followup} = SL::DB::FollowUp->new();
689
690   $self->{shipto} = SL::DB::Shipto->new();
691
692   $self->{contact} = SL::DB::Contact->new();
693 }
694
695 sub _pre_render {
696   my ($self) = @_;
697
698   my $dbh = $::form->get_standard_dbh();
699
700   my $query;
701
702   $self->{all_business} = SL::DB::Manager::Business->get_all();
703
704   $self->{all_employees} = SL::DB::Manager::Employee->get_all();
705
706   $query =
707     'SELECT DISTINCT(greeting)
708      FROM customer
709      WHERE greeting IS NOT NULL AND greeting != \'\'
710      UNION
711        SELECT DISTINCT(greeting)
712        FROM vendor
713        WHERE greeting IS NOT NULL AND greeting != \'\'
714      ORDER BY greeting';
715   $self->{all_greetings} = [
716     map(
717       { $_->{greeting}; }
718       selectall_hashref_query($::form, $dbh, $query)
719     )
720   ];
721
722   $query =
723     'SELECT DISTINCT(cp_title) AS title
724      FROM contacts
725      WHERE cp_title IS NOT NULL AND cp_title != \'\'
726      ORDER BY cp_title';
727   $self->{all_titles} = [
728     map(
729       { $_->{title}; }
730       selectall_hashref_query($::form, $dbh, $query)
731     )
732   ];
733
734   $self->{all_currencies} = SL::DB::Manager::Currency->get_all();
735
736   $self->{all_languages} = SL::DB::Manager::Language->get_all();
737
738   $self->{all_taxzones} = SL::DB::Manager::TaxZone->get_all();
739
740   #Employee:
741   #TODO: ALL_SALESMAN
742   #TODO: ALL_SALESMAN_CUSTOMERS
743
744   $self->{all_payment_terms} = SL::DB::Manager::PaymentTerm->get_all();
745
746   $self->{all_pricegroups} = SL::DB::Manager::Pricegroup->get_all();
747
748   $query =
749     'SELECT DISTINCT(cp_abteilung) AS department
750      FROM contacts
751      WHERE cp_abteilung IS NOT NULL AND cp_abteilung != \'\'
752      ORDER BY cp_abteilung';
753   $self->{all_departments} = [
754     map(
755       { $_->{department}; }
756       selectall_hashref_query($::form, $dbh, $query)
757     )
758   ];
759
760   $self->{contacts} = $self->{cv}->contacts;
761   $self->{contacts} ||= [];
762
763   $self->{shiptos} = $self->{cv}->shipto;
764   $self->{shiptos} ||= [];
765
766   $self->{template_args} = {};
767
768   $::request->{layout}->add_javascripts('autocomplete_customer.js');
769 }
770
771 1;