Merge branch 'b-3.6.1' of ../kivitendo-erp_20220811
[kivitendo-erp.git] / SL / Controller / CustomerVendorTurnover.pm
1 package SL::Controller::CustomerVendorTurnover;
2 use strict;
3 use parent qw(SL::Controller::Base);
4 use SL::DBUtils;
5 use SL::DB::AccTransaction;
6 use SL::DB::Invoice;
7 use SL::DB::Order;
8 use SL::DB::EmailJournal;
9 use SL::DB::Letter;
10 use SL::DB;
11
12 __PACKAGE__->run_before('check_auth');
13
14 sub action_list_turnover {
15   my ($self) = @_;
16
17   return $self->render('generic/error', { layout => 0 }, label_error => "list_transactions needs a trans_id") unless $::form->{id};
18
19   my $cv = $::form->{id};
20   my $open_invoices;
21   if ( $::form->{db} eq 'customer' ) {
22     $open_invoices = SL::DB::Manager::Invoice->get_all(
23       query        => [
24                         customer_id => $cv,
25                         or          => [
26                                          amount => { gt => \'paid'},
27                                          amount => { lt => \'paid'},
28                                        ],
29                       ],
30       sort_by      => 'transdate DESC',
31       with_objects => [ 'dunnings' ],
32     );
33   } else {
34     $open_invoices = SL::DB::Manager::PurchaseInvoice->get_all(
35       query   => [
36                    vendor_id => $cv,
37                    or        => [
38                                   amount => { gt => \'paid'},
39                                   amount => { lt => \'paid'},
40                                 ],
41                  ],
42       sort_by => 'transdate DESC',
43     );
44   }
45   my $open_items;
46   if (@{$open_invoices}) {
47     $open_items = $self->_list_open_items($open_invoices);
48   }
49   my $open_orders = $self->_get_open_orders;
50   return $self->render('customer_vendor_turnover/turnover', { header => 0 },
51                        open_orders => $open_orders,
52                        open_items  => $open_items,
53                        id          => $cv,
54                       );
55 }
56
57 sub _list_open_items {
58   my ($self, $open_items) = @_;
59
60   return $self->render('customer_vendor_turnover/_list_open_items', { output => 0 },
61                         OPEN_ITEMS => $open_items,
62                         title      => $::locale->text('Open Items'),
63                       );
64 }
65
66 sub action_count_open_items_by_year {
67   my ($self) = @_;
68
69   return $self->render('generic/error', { layout => 0 }, label_error => "list_transactions needs a trans_id") unless $::form->{id};
70   my $dbh = SL::DB->client->dbh;
71
72   my $cv = $::form->{id};
73
74   my $query = <<SQL;
75    SELECT EXTRACT (YEAR FROM d.transdate),
76           count(d.id),
77           max(d.dunning_level)
78      FROM dunning d
79 LEFT JOIN ar a ON a.id = d.trans_id
80 LEFT JOIN customer c ON a.customer_id = c.id
81     WHERE c.id = ?
82  GROUP BY EXTRACT (YEAR FROM d.transdate), c.id
83  ORDER BY date_part DESC
84 SQL
85
86   $self->{dun_statistic} = selectall_hashref_query($::form, $dbh, $query, $cv);
87   $self->render('customer_vendor_turnover/count_open_items_by_year', { layout => 0 });
88 }
89
90 sub action_count_open_items_by_month {
91
92   my ($self) = @_;
93
94   return $self->render('generic/error', { layout => 0 }, label_error => "list_transactions needs a trans_id") unless $::form->{id};
95   my $dbh = SL::DB->client->dbh;
96
97   my $cv = $::form->{id};
98
99   my $query = <<SQL;
100    SELECT CONCAT(EXTRACT (MONTH FROM d.transdate),'/',EXTRACT (YEAR FROM d.transdate)) AS date_part,
101           count(d.id),
102           max(d.dunning_level)
103      FROM dunning d
104 LEFT JOIN ar a ON a.id = d.trans_id
105 LEFT JOIN customer c ON a.customer_id = c.id
106     WHERE c.id = ?
107  GROUP BY EXTRACT (YEAR FROM d.transdate), EXTRACT (MONTH FROM d.transdate), c.id
108  ORDER BY EXTRACT (YEAR FROM d.transdate) DESC
109 SQL
110
111    $self->{dun_statistic} = selectall_hashref_query($::form, $dbh, $query, $cv);
112    $self->render('customer_vendor_turnover/count_open_items_by_year', { layout => 0 });
113 }
114
115 sub action_turnover_by_month {
116
117   my ($self) = @_;
118
119   return $self->render('generic/error', { layout => 0 }, label_error => "list_transactions needs a trans_id") unless $::form->{id};
120
121   my $dbh = SL::DB->client->dbh;
122   my $cv = $::form->{id};
123   my ($db, $cv_type);
124   if ($::form->{db} eq 'customer') {
125     $db      = "ar";
126     $cv_type = "customer_id";
127   } else {
128     $db      = "ap";
129     $cv_type = "vendor_id";
130   }
131   my $query = <<SQL;
132   SELECT CONCAT(EXTRACT (MONTH FROM transdate),'/',EXTRACT (YEAR FROM transdate)) as date_part,
133          count(id)                                                                as count,
134          sum(amount)                                                              as amount,
135          sum(netamount)                                                           as netamount,
136          sum(paid)                                                                as paid
137     FROM $db WHERE $cv_type = ?
138 GROUP BY EXTRACT (YEAR FROM transdate), EXTRACT (MONTH FROM transdate)
139 ORDER BY EXTRACT (YEAR FROM transdate) DESC, EXTRACT (MONTH FROM transdate) DESC
140 SQL
141    $self->{turnover_statistic} = selectall_hashref_query($::form, $dbh, $query, $cv);
142    $self->render('customer_vendor_turnover/count_turnover', { layout => 0 });
143 }
144
145 sub action_turnover_by_year {
146   my ($self) = @_;
147
148   return $self->render('generic/error', { layout => 0 }, label_error => "list_transactions needs a trans_id") unless $::form->{id};
149
150   my $dbh = SL::DB->client->dbh;
151   my $cv = $::form->{id};
152   my ($db, $cv_type);
153   if ($::form->{db} eq 'customer') {
154     $db      = "ar";
155     $cv_type = "customer_id";
156   } else {
157     $db      = "ap";
158     $cv_type = "vendor_id";
159   }
160   my $query = <<SQL;
161   SELECT EXTRACT (YEAR FROM transdate) as date_part,
162          count(id)                     as count,
163          sum(amount)                   as amount,
164          sum(netamount)                as netamount,
165          sum(paid)                     as paid
166     FROM $db WHERE $cv_type = ?
167 GROUP BY date_part
168 ORDER BY date_part DESC
169 SQL
170    $self->{turnover_statistic} = selectall_hashref_query($::form, $dbh, $query, $cv);
171    $self->render('customer_vendor_turnover/count_turnover', { layout => 0 });
172 }
173
174 sub action_get_invoices {
175   my ($self) = @_;
176
177   return $self->render('generic/error', { layout => 0 }, label_error => "list_transactions needs a trans_id") unless $::form->{id};
178
179   my $cv = $::form->{id};
180   my $invoices;
181   if ( $::form->{db} eq 'customer' ) {
182     $invoices = SL::DB::Manager::Invoice->get_all(
183       query   => [ customer_id => $cv, ],
184       sort_by => 'transdate DESC',
185     );
186   } else {
187     $invoices = SL::DB::Manager::PurchaseInvoice->get_all(
188       query   => [ vendor_id => $cv, ],
189       sort_by => 'transdate DESC',
190     );
191   }
192   $self->render('customer_vendor_turnover/invoices_statistic', { layout => 0 }, invoices => $invoices);
193 }
194
195 sub action_get_orders {
196   my ($self) = @_;
197
198   return $self->render('generic/error', { layout => 0 }, label_error => "list_transactions needs a trans_id") unless $::form->{id};
199
200   my $cv = $::form->{id};
201   my $orders;
202   my $type = $::form->{type};
203   if ( $::form->{db} eq 'customer' ) {
204     $orders = SL::DB::Manager::Order->get_all(
205       query   => [
206                    customer_id => $cv,
207                    quotation   => ($type eq 'quotation' ? 'T' : 'F')
208                  ],
209       sort_by => 'transdate DESC',
210     );
211   } else {
212     $orders = SL::DB::Manager::Order->get_all(
213       query   => [
214                    vendor_id => $cv,
215                    quotation => ($type eq 'quotation' ? 'T' : 'F')
216                  ],
217       sort_by => 'transdate DESC',
218     );
219   }
220   if ( $type eq 'order') {
221     $self->render('customer_vendor_turnover/order_statistic', { layout => 0 }, orders => $orders);
222   } else {
223     $self->render('customer_vendor_turnover/quotation_statistic', { layout => 0 }, orders => $orders);
224   }
225 }
226
227 sub _get_open_orders {
228   my ( $self ) = @_;
229
230   return $self->render('generic/error', { layout => 0 }, label_error => "list_transactions needs a trans_id") unless $::form->{id};
231   my $open_orders;
232   my $cv = $::form->{id};
233
234   if ( $::form->{db} eq 'customer' ) {
235     $open_orders = SL::DB::Manager::Order->get_all(
236       query   => [
237                    customer_id => $cv,
238                    closed      => 'F',
239                  ],
240       sort_by => 'transdate DESC',
241     );
242   } else {
243     $open_orders = SL::DB::Manager::Order->get_all(
244       query   => [
245                    vendor_id => $cv,
246                    closed    => 'F',
247                  ],
248       sort_by => 'transdate DESC',
249     );
250   }
251
252   return 0 unless scalar @{$open_orders};
253   return $self->render('customer_vendor_turnover/_list_open_orders', { output => 0 },
254                         orders => $open_orders,
255                         title  => $::locale->text('Open Orders'),
256                       );
257 }
258
259 sub action_get_mails {
260   my ( $self ) = @_;
261
262   return $self->render('generic/error', { layout => 0 }, label_error => "list_transactions needs a trans_id") unless $::form->{id};
263   my $dbh = SL::DB->client->dbh;
264   my $query;
265   my $cv = $::form->{id};
266
267   if ( $::form->{db} eq 'customer') {
268     $query = <<SQL;
269 WITH
270 oe_emails_customer
271        AS (SELECT rc.to_id, rc.from_id, oe.quotation, oe.quonumber, oe.ordnumber, c.id
272      FROM record_links rc
273 LEFT JOIN oe oe      ON rc.from_id = oe.id
274 LEFT JOIN customer c ON oe.customer_id = c.id
275     WHERE rc.to_table = 'email_journal'
276       AND rc.from_table ='oe'),
277
278 do_emails_customer
279        AS (SELECT rc.to_id, rc.from_id, o.donumber, c.id
280      FROM record_links rc
281 LEFT JOIN delivery_orders o ON rc.from_id = o.id
282 LEFT JOIN customer c ON o.customer_id = c.id
283     WHERE rc.to_table = 'email_journal'
284       AND rc.from_table = 'delivery_orders'),
285
286 inv_emails_customer
287        AS (SELECT rc.to_id, rc.from_id, inv.type, inv.invnumber, c.id
288      FROM record_links rc
289 LEFT JOIN ar inv ON rc.from_id = inv.id
290 LEFT JOIN customer c ON inv.customer_id = c.id
291     WHERE rc.to_table = 'email_journal'
292       AND rc.from_table = 'ar'),
293
294 letter_emails_customer
295        AS (SELECT rc.to_id, rc.from_id, l.letternumber, c.id
296      FROM record_links rc
297 LEFT JOIN letter l ON rc.from_id = l.id
298 LEFT JOIN customer c ON l.customer_id = c.id
299     WHERE rc.to_table = 'email_journal'
300       AND rc.from_table = 'letter')
301
302 SELECT ej.*,
303  CASE
304   oec.quotation WHEN 'F' THEN 'Sales Order'
305                 ELSE 'Quotation'
306  END AS type,
307  CASE
308   oec.quotation WHEN 'F' THEN oec.ordnumber
309                 ELSE oec.quonumber
310  END    AS recordnumber,
311  oec.id AS record_id
312      FROM email_journal ej
313 LEFT JOIN oe_emails_customer oec ON ej.id = oec.to_id
314     WHERE oec.id = ?
315
316 UNION
317
318 SELECT ej.*, 'Delivery Order' AS type, dec.donumber AS recordnumber,dec.id AS record_id
319      FROM email_journal ej
320 LEFT JOIN do_emails_customer dec ON ej.id = dec.to_id
321     WHERE dec.id = ?
322
323 UNION
324
325 SELECT ej.*,
326  CASE
327   iec.type WHEN 'credit_note' THEN 'Credit Note'
328            WHEN 'invoice' THEN 'Invoice'
329            ELSE 'N/A'
330  END           AS type,
331  iec.invnumber AS recordnumber,
332         iec.id AS record_id
333      FROM email_journal ej
334 LEFT JOIN inv_emails_customer iec ON ej.id = iec.to_id
335     WHERE iec.id = ?
336
337 UNION
338
339 SELECT ej.*, 'Letter' AS type, lec.letternumber AS recordnumber,lec.id AS record_id
340      FROM email_journal ej
341 LEFT JOIN letter_emails_customer lec ON ej.id = lec.to_id
342     WHERE lec.id = ?
343  ORDER BY sent_on DESC
344 SQL
345   }
346   else {
347     $query = <<SQL;
348 WITH
349 oe_emails_vendor
350        AS (SELECT rc.to_id, rc.from_id, oe.quotation, oe.quonumber, oe.ordnumber, c.id
351      FROM record_links rc
352 LEFT JOIN oe oe ON rc.from_id = oe.id
353 LEFT JOIN vendor c ON oe.vendor_id = c.id
354     WHERE rc.to_table = 'email_journal'
355       AND rc.from_table ='oe'),
356
357 do_emails_vendor
358        AS (SELECT rc.to_id, rc.from_id, o.donumber, c.id
359      FROM record_links rc
360 LEFT JOIN delivery_orders o ON rc.from_id = o.id
361 LEFT JOIN vendor c ON o.vendor_id = c.id
362     WHERE rc.to_table = 'email_journal'
363       AND rc.from_table = 'delivery_orders'),
364
365 inv_emails_vendor
366        AS (SELECT rc.to_id, rc.from_id, inv.type, inv.invnumber, c.id
367      FROM record_links rc
368 LEFT JOIN ap inv ON rc.from_id = inv.id
369 LEFT JOIN vendor c ON inv.vendor_id = c.id
370     WHERE rc.to_table = 'email_journal'
371       AND rc.from_table = 'ar'),
372
373 letter_emails_vendor
374        AS (SELECT rc.to_id, rc.from_id, l.letternumber, c.id
375      FROM record_links rc
376 LEFT JOIN letter l ON rc.from_id = l.id
377 LEFT JOIN vendor c ON l.vendor_id = c.id
378     WHERE rc.to_table = 'email_journal'
379       AND rc.from_table = 'letter')
380
381 SELECT ej.*,
382  CASE
383   oec.quotation WHEN 'F' THEN 'Purchase Order'
384                 ELSE 'Request quotation'
385  END AS type,
386  CASE
387   oec.quotation WHEN 'F' THEN oec.ordnumber
388                 ELSE oec.quonumber
389  END   AS recordnumber,
390 oec.id AS record_id
391      FROM email_journal ej
392 LEFT JOIN oe_emails_vendor oec ON ej.id = oec.to_id
393     WHERE oec.id = ?
394
395 UNION
396
397 SELECT ej.*, 'Purchase Delivery Order' AS type, dec.donumber AS recordnumber, dec.id AS record_id
398      FROM email_journal ej
399 LEFT JOIN do_emails_vendor dec ON ej.id = dec.to_id
400     WHERE dec.id = ?
401
402 UNION
403
404 SELECT ej.*, iec.type AS type, iec.invnumber AS recordnumber, iec.id AS record_id
405      FROM email_journal ej
406 LEFT JOIN inv_emails_vendor iec ON ej.id = iec.to_id
407     WHERE iec.id = ?
408
409 UNION
410
411 SELECT ej.*, 'Letter' AS type, lec.letternumber AS recordnumber, lec.id AS record_id
412      FROM email_journal ej
413 LEFT JOIN letter_emails_vendor lec ON ej.id = lec.to_id
414     WHERE lec.id = ?
415  ORDER BY sent_on DESC
416 SQL
417   }
418   my $emails = selectall_hashref_query($::form, $dbh, $query, $cv, $cv, $cv, $cv);
419   $self->render('customer_vendor_turnover/email_statistic', { layout => 0 }, emails => $emails);
420 }
421
422 sub action_get_letters {
423   my ($self) = @_;
424
425   return $self->render('generic/error', { layout => 0 }, label_error => "list_transactions needs a trans_id") unless $::form->{id};
426
427   my $cv = $::form->{id};
428   my $letters;
429   my $type = $::form->{type};
430   if ( $::form->{db} eq 'customer' ) {
431     $letters = SL::DB::Manager::Letter->get_all(
432       query   => [ customer_id => $cv, ],
433       sort_by => 'date DESC',
434     );
435   } else {
436     $letters = SL::DB::Manager::Letter->get_all(
437       query   => [ vendor_id => $cv, ],
438       sort_by => 'date DESC',
439     );
440   }
441     $self->render('customer_vendor_turnover/letter_statistic', { layout => 0 }, letters => $letters);
442 }
443
444 sub check_auth {
445   $::auth->assert('show_extra_record_tab_customer | show_extra_record_tab_vendor');
446 }
447
448 1;
449
450 __END__
451
452 =encoding utf-8
453
454 =head1 NAME
455
456 SL::Controller::CustomerVendorTurnover
457
458 =head1 DESCRIPTION
459
460 Gets all kinds of records like orders, request orders, quotations, invoices, emails, letters
461
462 wich belong to customer/vendor and displays them in an extra tab "Records".
463
464 =head1 URL ACTIONS
465
466 =over 4
467
468 =item C<action_list_turnover>
469
470 Basic action wich displays open invoices and open orders if there are any and shows the tab menu for the other actions
471
472 =item C<action_count_open_items_by_month>
473
474 gets and shows a dunning statistic of the customer by month
475
476 =item C<action_count_open_items_by_year>
477
478 gets and shows a dunning statistic of the customer by year
479
480 =item C<action_turnover_by_month>
481
482 gets and shows an invoice statistic of customer/vendor by month
483
484 =item C<action_turnover_by_year>
485
486 gets and shows an invoice statistic of customer/vendor by year
487
488 =item C<action_get_invoices>
489
490 get and shows all invoices from the customer/vendor in an extra tab
491
492 =item C<action_get_orders>
493
494 get and shows all orders from the customer/vendor in an extra tab
495
496 =item C<action_get_letters>
497
498 get and shows all letters from the customer/vendor in an extra tab
499
500 =item C<action_get_mails>
501
502 get and shows all mails from the customer/vendor in an extra tab
503
504 =back
505
506 =head1 Functions
507
508 =over 4
509
510 =item C<_get_open_orders>
511
512 retrieves the open orders for customer/vendor to display them
513
514 =item C<_list_open_items>
515
516 retrieves open invoices with their dunnings to display them
517
518 =back
519
520 =head1 BUGS
521
522 None yet. :)
523
524 =head1 AUTHOR
525
526 W. Hahn E<lt>wh@futureworldsearch.netE<gt>
527
528 =cut