1 <?php
2
3 namespace rpf\extension\module;
4
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
44 class mgntRatioExport extends extensionModule
45 {
46
47 protected $costs = array();
48
49
50 protected $sum = array();
51
52
53 protected $product = array();
54
55
56 protected $total = array();
57
58 59 60 61 62
63 public function __construct(extension &$system)
64 {
65 parent::__construct($system);
66 $this->data['costs'] =& $this->costs;
67 $this->data['sum'] =& $this->sum;
68 $this->data['product'] =& $this->product;
69 $this->data['total'] =& $this->total;
70 }
71
72 73 74 75 76 77
78 public function setPurchasePrices(array $prices)
79 {
80 $this->costs = $prices;
81 return $this;
82 }
83
84
85 86 87 88 89 90
91 protected function calcOverall()
92 {
93 foreach ($this->system->orders->loadAll(['accounting' => 1])->getData() as $order)
94 {
95 foreach ($order['dispositions'] as $dispo)
96 {
97
98 if ((strtotime($dispo['account_end']) != 0 && strtotime($dispo['account_end']) < time())) {
99 continue;
100 }
101
102 $sum =& $this->sum[$dispo['product']['norm']][$dispo['product']['pronr']];
103
104
105 $sum['count'];
106
107
108 $sum['countBilled'];
109
110
111 $sum['countUnbilled'];
112
113
114 $sum['turnoverRegular'];
115
116
117 $sum['turnoverBilled'];
118
119
120 $sum['systemPoints'];
121
122
123 $sum['systemPointsBilled'];
124
125
126 $sum['systemPointsUnbilled'];
127
128
129 $sum['externalCosts'];
130
131
132 $sum['externalCostsBilled'];
133
134
135 $sum['externalCostsUnbilled'];
136
137
138 $sum['workingTime'];
139
140
141 $sum['workingTimeBilled'];
142
143
144 $sum['workingTimeUnbilled'];
145
146
147 $sum['workingTimeCosts'];
148
149
150 $sum['workingTimeCostsBilled'];
151
152
153 $sum['workingTimeCostsUnbilled'];
154
155
156 $sum['orders'][$order['ordnr']] += 1;
157
158
159
160 if (isset($this->costs[$dispo['product']['norm']][$dispo['product']['pronr']]['externalCosts']))
161 {
162 $externalCosts = $this->costs[$dispo['product']['norm']][$dispo['product']['pronr']]['externalCosts'] * $dispo['amount'];
163 }
164 else if (isset($this->costs[$dispo['product']['norm']]['externalCosts']))
165 {
166 $externalCosts = $this->costs[$dispo['product']['norm']]['externalCosts'] * $dispo['amount'];
167 }
168 else
169 {
170 $externalCosts = 0;
171 }
172
173
174 if (isset($this->costs[$dispo['product']['norm']][$dispo['product']['pronr']]['workingTime']))
175 {
176 $workingTime = $this->costs[$dispo['product']['norm']][$dispo['product']['pronr']]['workingTime'] * $dispo['amount'];
177 }
178 else if (isset($this->costs[$dispo['product']['norm']]['workingTime']))
179 {
180 $workingTime = $this->costs[$dispo['product']['norm']]['workingTime'] * $dispo['amount'];
181 }
182 else
183 {
184 $workingTime = 0;
185 }
186
187
188 $purchasePrice = isset($this->costs[$dispo['product']['norm']][$dispo['product']['pronr']]) ? $this->costs[$dispo['product']['norm']][$dispo['product']['pronr']] : 0;
189
190 $systemPoints = isset($purchasePrice['systemPoints']) ? $purchasePrice['systemPoints'] * $dispo['amount'] : 0;
191 $workingTimeCosts = isset($this->costs['workingTimeCosts']) ? $this->costs['workingTimeCosts'] * $workingTime : 0;
192
193
194 195 196
197 if ($dispo['price']['default_net'] != 0)
198 {
199 $sum['turnoverRegular'] += $dispo['price']['default_net'] * $dispo['amount'];
200 $sum['turnoverBilled'] += $dispo['price']['unit_net'] * $dispo['amount'];
201
202 $sum['countBilled'] += $dispo['amount'];
203 $sum['systemPointsBilled'] += $systemPoints;
204 $sum['externalCostsBilled'] += $externalCosts;
205 $sum['workingTimeBilled'] += $workingTime;
206 $sum['workingTimeCostsBilled'] += $workingTimeCosts;
207 }
208
209 210 211
212 else
213 {
214 $sum['countUnbilled'] += $dispo['amount'];
215 $sum['systemPointsUnbilled'] += $systemPoints;
216 $sum['externalCostsUnbilled'] += $externalCosts;
217 $sum['workingTimeUnbilled'] += $workingTime;
218 $sum['workingTimeCostsUnbilled'] += $workingTimeCosts;
219 }
220
221 222 223
224 $sum['count'] = $sum['countBilled'] + $sum['countUnbilled'];
225 $sum['systemPoints'] = $sum['systemPointsBilled'] + $sum['systemPointsUnbilled'];
226 $sum['externalCosts'] = $sum['externalCostsBilled'] + $sum['externalCostsUnbilled'];
227 $sum['workingTime'] = $sum['workingTimeBilled'] + $sum['workingTimeUnbilled'];
228 $sum['workingTimeCosts'] = $sum['workingTimeCostsBilled'] + $sum['workingTimeCostsUnbilled'];
229 }
230 }
231 return $this;
232 }
233
234 235 236 237 238 239 240
241 protected function calcOverallOverhead()
242 {
243 if (empty($this->sum)) throw new \Exception('$this->sum[] is empty. You have to run ::calcOverall first AND should have some active dispositions', 424);
244
245 foreach ($this->sum as $type => &$products)
246 {
247 foreach ($products as $id => &$sum)
248 {
249
250 $sum['discount'] = $sum['turnoverRegular']-$sum['turnoverBilled'];
251
252
253 $sum['overheadCosts'] = 0;
254
255
256 $sum['overheadCostsBilled'] = 0;
257
258
259 $sum['overheadCostsUnbilled'] = 0;
260
261 if ($this->total['systemPoints'] > 0)
262 {
263 $sum['overheadCosts'] = $this->costs['overheadCosts'] / $this->total['systemPoints'] * $sum['systemPoints'];
264 $sum['overheadCostsBilled'] = $this->costs['overheadCosts'] / $this->total['systemPointsBilled'] * $sum['systemPointsBilled'];
265 $sum['overheadCostsUnbilled'] = $this->costs['overheadCosts'] / $this->total['systemPointsUnbilled'] * $sum['systemPointsUnbilled'];
266 }
267
268
269 $sum['unbilledCosts'] = $sum['externalCostsUnbilled']+$sum['workingTimeCostsUnbilled']+$sum['overheadCostsUnbilled'];
270 }
271 }
272 return $this;
273 }
274
275 276 277 278 279 280 281
282 protected function calcOverallContributionMargin()
283 {
284 if (empty($this->data['sum'])) throw new \Exception('$this->sum[] is empty. You have to run ::calcOverall first AND should have some active dispositions', 424);
285
286 foreach ($this->data['sum'] as $type => &$products)
287 {
288 foreach ($products as $id => &$sum)
289 {
290 if ($sum['turnoverRegular'] > 0)
291 {
292 $sum['unbilledCostsShare'] = $this->total['unbilledCosts'] / $this->total['systemPointsBilled'] * $sum['systemPointsBilled'];
293
294 $sum['cm1'] = $sum['turnoverRegular'] - $sum['externalCostsBilled'];
295 $sum['cm2'] = $sum['cm1'] - $sum['workingTimeCostsBilled'];
296 $sum['cm3'] = $sum['cm2'] - $sum['overheadCostsBilled'] - $sum['unbilledCostsShare'];
297 $sum['cm4'] = $sum['cm3'] - $sum['discount'];
298 $sum['cm5'] = $sum['cm4'];
299 $sum['displayUnbilledCosts'] = $sum['unbilledCosts'];
300 $sum['margin'] = 100/$sum['turnoverRegular']*$sum['cm3'];
301 }
302 else
303 {
304 $sum['cm1'] = $sum['turnoverRegular'] - $sum['externalCostsUnbilled'];
305 $sum['cm2'] = $sum['cm1'] - $sum['workingTimeCostsUnbilled'];
306 $sum['cm3'] = $sum['cm2'] - $sum['overheadCostsUnbilled'];
307 $sum['cm4'] = $sum['cm3'] - $sum['discount'];
308 $sum['cm5'] = $sum['cm4'] + $sum['unbilledCosts'];
309 $sum['displayUnbilledCosts'] = 0;
310 $sum['margin'] = 0;
311 }
312 $sum['displayUnbilledCredit'] = $sum['unbilledCosts'];
313 }
314 }
315 return $this;
316 }
317
318 319 320 321 322 323 324
325 protected function calcProducts()
326 {
327 if (empty($this->sum)) throw new \Exception('$this->sum[] is empty. You have to run ::calcOverall first AND should have some active dispositions', 424);
328
329 foreach ($this->sum as $type => &$products)
330 {
331 foreach ($products as $id => &$sum)
332 {
333 $p =& $this->product[$type][$id];
334
335
336 $p['type'] = $type;
337
338
339 $p['id'] = $id;
340
341
342 $p['externalCosts'] = 0;
343
344
345 $p['workingTimeCosts'] = 0;
346
347
348 $p['count'] = 0;
349
350
351 $p['countBilled'] = 0;
352
353
354 $p['countUnbilled'] = 0;
355
356
357 $p['turnoverRegular'] = 0;
358
359
360 $p['turnoverBilled'] = 0;
361
362
363 $p['externalCosts'] = 0;
364
365
366 $p['workingTimeCosts'] = 0;
367
368
369 $p['workingTime'] = 0;
370
371
372 $p['overheadCosts'] = 0;
373
374 $p['cm1'] = 0;
375 $p['cm2'] = 0;
376 $p['cm3'] = 0;
377 $p['bestPrice'] = 0;
378
379 if ($sum['countBilled'] > 0)
380 {
381 $p['turnoverRegular'] = $sum['turnoverRegular'] / $sum['countBilled'];
382 $p['turnoverBilled'] = $sum['turnoverBilled'] / $sum['countBilled'];
383 $p['externalCosts'] = $sum['externalCostsBilled'] / $sum['countBilled'];
384 $p['workingTime'] = $sum['workingTimeBilled'] / $sum['countBilled'];
385 $p['workingTimeCosts'] = $sum['workingTimeCostsBilled'] / $sum['countBilled'];
386 $p['overheadCosts'] = $sum['overheadCostsBilled'] / $sum['countBilled'];
387 $p['unbilledCostsShare'] = $sum['unbilledCostsShare'] / $sum['countBilled'];
388
389 $p['cm1'] = $sum['cm1'] / $sum['countBilled'];
390 $p['cm2'] = $sum['cm2'] / $sum['countBilled'];
391 $p['cm3'] = $sum['cm3'] / $sum['countBilled'];
392
393 $p['bestPrice'] = ($sum['externalCostsBilled']+$sum['workingTimeCostsBilled']+$sum['overheadCostsBilled']+$sum['unbilledCostsShare'])/$sum['countBilled']*1.15;
394 }
395 else
396 {
397 $p['externalCosts'] = $sum['externalCostsUnbilled'] / $sum['countUnbilled'];
398 $p['workingTime'] = $sum['workingTimeUnbilled'] / $sum['countUnbilled'];
399 $p['workingTimeCosts']= $sum['workingTimeCostsUnbilled'] / $sum['countUnbilled'];
400 $p['overheadCosts'] = $sum['overheadCostsUnbilled'] / $sum['countUnbilled'];
401 $p['unbilledCosts'] = $sum['unbilledCosts'] / $sum['countUnbilled'];
402
403 $p['cm1'] = $sum['cm1'] / $sum['countUnbilled'];
404 $p['cm2'] = $sum['cm2'] / $sum['countUnbilled'];
405 $p['cm3'] = $sum['cm3'] / $sum['countUnbilled'];
406
407 $p['bestPrice'] = ($sum['externalCostsUnbilled']+$sum['workingTimeCostsUnbilled']+$sum['overheadCostsUnbilled'])/$sum['countUnbilled']*1.15;
408 }
409
410 foreach ($sum['orders'] as $ordnr => $count)
411 {
412 $p['orders'] .= "$ordnr({$count}x), ";
413 }
414 }
415 }
416 return $this;
417 }
418
419 420 421 422 423
424 protected function calcTotal()
425 {
426 $this->total = array();
427
428 foreach ($this->sum as $type => &$products)
429 {
430 foreach ($products as $name => &$product)
431 {
432 foreach ($product as $key => &$val)
433 {
434 if (is_int($val) OR is_float($val))
435 {
436 $this->total[$key] += $val;
437 }
438 }
439 }
440 }
441
442 $this->total['margin'] = $sum['margin'] = 100/$this->total['turnoverRegular']*$this->total['cm3'];
443 return $this;
444 }
445
446
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
463 public function calc()
464 {
465 $this->calcOverall();
466 $this->calcTotal();
467
468 $this->calcOverallOverhead();
469 $this->calcTotal();
470
471 $this->calcOverallContributionMargin();
472 $this->calcTotal();
473
474 $this->calcProducts();
475 $this->calcTotal();
476
477 return $this;
478 }
479
480
481 482 483 484 485
486 public function getCsvGe()
487 {
488 $csv =
489 'Produkttyp;'.
490 'Produktname;'.
491 'Anzahl;'.
492 ';'.
493
494 '�-Listenpreis (Stk./Mon.);'.
495 ' - Fremdkosten;'.
496 'db1;'.
497 ';'.
498
499 ' - Personalkosten (Mon.);'.
500 '(Personalaufwand/Jahr);'.
501 'db2;'.
502 ';'.
503
504 ' - Gemeinkosten (Srv.-Infrastruktur, ...);'.
505 ' - Verrechnugsgemeinkosten (Inkl.-Domain, ...);'.
506 'db3;'.
507 'Marge (Listenpreis/db3);'.
508 ';'.
509
510 'x verkaufter Anzahl;'.
511 ' - Rabatt;'.
512 'DB4;'.
513 ';'.
514
515 ' - Verrechnungseinzelkosten;'.
516 ' + Verrechnungseinzelgutschrift;'.
517 'DB5;'.
518 ';'.
519
520 'Niedrigstpreis (Kosten+15% / Stk./Mon.); '.
521 '�-Verkaufspreis (Stk./Mon.);'.
522 'Umsatz (Gesamt/Mon.); '.
523 ';'.
524
525 'RP2-Auft�ge;'.
526 "\n\n";
527
528 foreach ($this->data['product'] as $type => &$products)
529 {
530 ksort($products);
531 foreach ($products as $name => &$product)
532 {
533
534 $sum =& $this->sum[$type][$name];
535 $total =& $this->total;
536
537 $csv .= "$type;";
538 $csv .= "$name;";
539 $csv .= number_format($sum['count'], 0, ',', '.').';';
540 $csv .= ';';
541
542 $csv .= self::getEuroFormattedCsvColumn($product['turnoverRegular']);
543 $csv .= self::getEuroFormattedCsvColumn(-$product['externalCosts']);
544 $csv .= self::getEuroFormattedCsvColumn($product['cm1']);
545 $csv .= ';';
546
547 $csv .= self::getEuroFormattedCsvColumn(-$product['workingTimeCosts']);
548 $csv .= date("H:i", mktime(0,0,0)+$product['workingTime']*60*60*12) .'; ';
549 $csv .= self::getEuroFormattedCsvColumn($product['cm2']);
550 $csv .= ';';
551
552 $csv .= self::getEuroFormattedCsvColumn(-$product['overheadCosts']);
553 $csv .= self::getEuroFormattedCsvColumn(-$product['unbilledCostsShare']);
554 $csv .= self::getEuroFormattedCsvColumn($product['cm3']);
555 $csv .= number_format($sum['margin'], 0, ',', '.').' %;';
556 $csv .= ';';
557
558 $csv .= $sum['countBilled'].';';
559 $csv .= self::getEuroFormattedCsvColumn(-$sum['discount']);
560 $csv .= self::getEuroFormattedCsvColumn($sum['cm4']);
561 $csv .= ';';
562
563 $csv .= self::getEuroFormattedCsvColumn(-$sum['displayUnbilledCosts']);
564 $csv .= self::getEuroFormattedCsvColumn($sum['displayUnbilledCredit']);
565 $csv .= self::getEuroFormattedCsvColumn($sum['cm5']);
566 $csv .= ';';
567
568
569 $csv .= self::getEuroFormattedCsvColumn($product['bestPrice']);
570 $csv .= self::getEuroFormattedCsvColumn($product['turnoverBilled']);
571 $csv .= self::getEuroFormattedCsvColumn($sum['turnoverRegular']);
572 $csv .= ';';
573
574 $csv .= $product['orders'];
575 $csv .= "\n";
576 }
577 $csv .= "\n";
578 }
579
580 $csv .=
581 '**SUMME**;'.
582 ';'.
583 ';'.
584 ';'.
585
586 self::getEuroFormattedCsvColumn($total['turnoverRegular']).
587 self::getEuroFormattedCsvColumn(-$total['externalCosts']).
588 self::getEuroFormattedCsvColumn($total['cm1']).
589 ';'.
590
591 self::getEuroFormattedCsvColumn($total['workingTimeCosts']).
592
593 round($total['workingTime'],0).'h / Mon;'.
594 self::getEuroFormattedCsvColumn($total['cm2']).
595 ';'.
596
597 self::getEuroFormattedCsvColumn(-$total['overheadCosts']).
598 self::getEuroFormattedCsvColumn(-$total['unbilledCostsShare']).
599 self::getEuroFormattedCsvColumn($total['cm3']).
600 number_format($total['margin'], 0, ',', '.').' %;'.
601 ';'.
602
603 ';'.
604 self::getEuroFormattedCsvColumn(-$total['discount']).
605 self::getEuroFormattedCsvColumn($total['cm4']).
606 ';'.
607
608 ';'.
609 ';'.
610 self::getEuroFormattedCsvColumn($total['cm5']).
611 ';'.
612
613
614 ';'.
615 self::getEuroFormattedCsvColumn($total['turnoverBilled']).
616 self::getEuroFormattedCsvColumn($total['turnoverRegular']);
617
618 $csv .= "\n\n\n\nGenerated with mgntRatioExport: https://github.com/ADoebeling/RP2-Toolbox \nCopyright (C) 2015 by Andreas D�beling <ad@1601.com> - 1601.production, Siegler&Th�mmler ohg, Erlangen";
619
620 return $csv;
621 }
622
623 624 625 626 627 628
629 public static function getEuroFormattedCsvColumn($float)
630 {
631 return number_format($float, 2, ',', '.').' �; ';
632 }
633
634 635 636 637 638 639 640
641 public function store()
642 {
643 throw new \Exception("Sorry, not implemented yet", 501);
644 return $this;
645 }
646
647
648 649 650 651 652 653 654 655
656 public function sendDownloadCsv($filename = NULL, $source = 'ge')
657 {
658 $filename = $filename !== NULL ? $filename : 'Hosting_MgntRatioExport_'.date('ymd').'_1SRV.csv';
659
660 if ($source == 'ge')
661 {
662 $csv = $this->getCsvGe();
663 }
664 else
665 {
666 throw new \Exception("Sorry, not implemented yet", 501);
667 }
668
669 header('Content-Type: application/csv');
670 header("Content-Disposition: attachment; filename=\"$filename\";");
671 echo $csv;
672 return $this;
673 }
674 }