|  | <?php | 
        
          |  | /* | 
        
          |  | * Author: Paweł Abramowicz <contact me using https://abramowicz.website> | 
        
          |  | * Double licensed under WTFPL/Beerware | 
        
          |  | * Do Whatever The Fun you want, and if you want, buy me a beer :) | 
        
          |  | */ | 
        
          |  |  | 
        
          |  | setlocale(LC_ALL, 'pl_PL.UTF-8', 'pl_PL', 'pl', 'Polish_Poland.28592'); | 
        
          |  |  | 
        
          |  | if (file_exists('config.json')) { | 
        
          |  | $config = json_decode(file_get_contents('config.json')); | 
        
          |  | } else { | 
        
          |  | $config = (object)[ | 
        
          |  | 'numbering' => '\R/Y/n/\1', | 
        
          |  | 'me' => (object)[ | 
        
          |  | 'name' => 'UZUPEŁNIJ PLIK KONFIGURACYJNY', | 
        
          |  | 'nip' => '0000000000', | 
        
          |  | 'address' => 'ul. Przykładowa xx, xxxxx Przykładowice', | 
        
          |  | 'tel' => '+48 xxxxxxxxx', | 
        
          |  | 'email' => 'xxxxxxxxxxxxxxxxxx', | 
        
          |  | 'bank' => 'xxxxxxxxxxxxxxxxxx', | 
        
          |  | 'iban' => 'xxxxxxxxxxxxxxxxxxxxxxxxxx', | 
        
          |  | ], | 
        
          |  | 'them' => (object)[ | 
        
          |  | 'name' => 'PRZYKŁADOWY KONTRAHENT sp. z o. o.', | 
        
          |  | 'nip' => '0000000000', | 
        
          |  | 'address' => 'ul. Innego Przykładu xx, xxxxx Przykładowice', | 
        
          |  | ], | 
        
          |  | 'item' => 'usługi programistyczne', | 
        
          |  | 'pkwiu' => '62.01.11.0', | 
        
          |  | 'salary' => 0, | 
        
          |  | 'clauses' => 'Sprzedawca zwolniony podmiotowo z podatku VAT (podatku od towarów i usług) zgodnie z art. 113 ust. 1', | 
        
          |  | ]; | 
        
          |  | if (!file_exists('config.dist.json')) { | 
        
          |  | file_put_contents( | 
        
          |  | 'config.dist.json', | 
        
          |  | json_encode( | 
        
          |  | $config, | 
        
          |  | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | 
        
          |  | ) | 
        
          |  | ); | 
        
          |  | } | 
        
          |  | } | 
        
          |  |  | 
        
          |  | if (!isset($_POST['hours'])): | 
        
          |  | ?> | 
        
          |  | <!DOCTYPE html> | 
        
          |  | <html lang="pl" dir="ltr"> | 
        
          |  | <head> | 
        
          |  | <meta charset="utf-8"> | 
        
          |  | <title>Wygeneruj fakturę</title> | 
        
          |  | <style> | 
        
          |  | body { font-family: sans-serif; } | 
        
          |  | .warning { padding: 1em; background: #fa0; } | 
        
          |  | .generate { padding: 1em; text-align: center; } | 
        
          |  | .item { padding: .5em; } | 
        
          |  | .item label { display: inline-block; min-width: 13em; text-align: left; } | 
        
          |  | .item input { border: none; border-bottom: 2px solid #ccc; width: 12em; } | 
        
          |  | .generate button { border: none; background: #456; color: #fff; font-weight: bold; padding: 1em 2em; width: 12em; } | 
        
          |  | footer { color: #ccc; text-align: center; } | 
        
          |  | footer a { color: #888; text-decoration: none; } | 
        
          |  | </style> | 
        
          |  | </head> | 
        
          |  | <body> | 
        
          |  | <?php if (!file_exists('config.json')): ?> | 
        
          |  | <div class="warning"> | 
        
          |  | UWAGA! Utworzono nowy plik konfiguracyjny. Skopiuj plik config.dist.json, nazwij go config.json i uzupełnij. | 
        
          |  | </div> | 
        
          |  | <?php endif; ?> | 
        
          |  | <form class="generate" action="" method="post"> | 
        
          |  | <div class="item"> | 
        
          |  | <label for="number">Numer rachunku:</label> | 
        
          |  | <input type="text" id="number" name="number" | 
        
          |  | value="<?php echo date($config->numbering); ?>"> | 
        
          |  | </div> | 
        
          |  | <div class="item"> | 
        
          |  | <label for="hours">Godziny:</label> | 
        
          |  | <input type="number" id="hours" name="hours" value="160" step="0.001"> | 
        
          |  | </div> | 
        
          |  | <div class="item"> | 
        
          |  | <label for="date">Data wystawienia:</label> | 
        
          |  | <input type="date" id="date" name="date" | 
        
          |  | value="<?php echo date('Y-m-d'); ?>"> | 
        
          |  | </div> | 
        
          |  | <div class="item"> | 
        
          |  | <label for="weekdays">Dni robocze na zapłatę:</label> | 
        
          |  | <input type="number" id="weekdays" name="weekdays" value="7"> | 
        
          |  | </div> | 
        
          |  | <div class="item"> | 
        
          |  | <label for="premiums">Dofinansowania (np. cowork):</label> | 
        
          |  | <input type="number" id="premiums" name="premiums" value="270"> | 
        
          |  | </div> | 
        
          |  | <div class="item"> | 
        
          |  | <button>Wygeneruj</button> | 
        
          |  | </div> | 
        
          |  | </form> | 
        
          |  | <!-- This footer does NOT show up on the invoice, don't remove it if you don't have to ;) --> | 
        
          |  | <footer>Code by <a href="https://abramowicz.website">Abramowicz</a></footer> | 
        
          |  | <!-- I can't force you, though! --> | 
        
          |  | </body> | 
        
          |  | </html> | 
        
          |  | <?php | 
        
          |  |  | 
        
          |  | else: | 
        
          |  |  | 
        
          |  | $hours = $_POST['hours']; | 
        
          |  | $amount = $hours * ($config->salary ?? 0) + ($_POST['premiums'] ?? 0); | 
        
          |  | $today = new \DateTime($_POST['date'] ?? 'today'); | 
        
          |  | $term = clone $today; | 
        
          |  | $term->modify(sprintf( | 
        
          |  | '+%d weekdays', | 
        
          |  | $_POST['weekdays'] ?? 7 | 
        
          |  | )); | 
        
          |  |  | 
        
          |  | [$words_amount, $remainder] = wording($amount); | 
        
          |  | ?> | 
        
          |  | <!DOCTYPE html> | 
        
          |  | <html lang="pl" dir="ltr"> | 
        
          |  | <head> | 
        
          |  | <meta charset="utf-8"> | 
        
          |  | <title>Rachunek nr <?php echo $_POST['number'] ?? date($config->numbering); ?></title> | 
        
          |  | <link rel="stylesheet" href="invoice.css"> | 
        
          |  | </head> | 
        
          |  | <body> | 
        
          |  |  | 
        
          |  | <h1>Rachunek nr <?php echo $_POST['number'] ?? date($config->numbering); ?></h1> | 
        
          |  |  | 
        
          |  | <h2>Sprzedawca</h2> | 
        
          |  | <p><?php echo $config->me->name; ?></p> | 
        
          |  | <p>NIP <?php echo $config->me->nip; ?></p> | 
        
          |  | <p><?php echo $config->me->address; ?></p> | 
        
          |  | <p>tel. <?php echo $config->me->tel; ?></p> | 
        
          |  | <p><?php echo $config->me->email; ?></p> | 
        
          |  | <p>bank: <?php echo $config->me->bank; ?></p> | 
        
          |  | <p>nr konta: <?php echo $config->me->iban; ?></p> | 
        
          |  |  | 
        
          |  | <h2>Nabywca</h2> | 
        
          |  | <p><?php echo $config->them->name; ?></p> | 
        
          |  | <p>NIP <?php echo $config->them->nip; ?></p> | 
        
          |  | <p><?php echo $config->them->address; ?></p> | 
        
          |  |  | 
        
          |  | <h2>Termin</h2> | 
        
          |  | <p>miejsce wystawienia: Wrocław</p> | 
        
          |  | <p>data sprzedaży: <?php echo strftime("%e %B %Y", $today->getTimestamp()); ?></p> | 
        
          |  | <p>data wystawienia: <?php echo strftime("%e %B %Y", $today->getTimestamp()); ?></p> | 
        
          |  | <p>termin płatności: <?php echo strftime("%e %B %Y", $term->getTimestamp()); ?></p> | 
        
          |  |  | 
        
          |  | <h2>Usługi</h2> | 
        
          |  | <table> | 
        
          |  | <tr class="header"> | 
        
          |  | <td>Nazwa</td> | 
        
          |  | <td>PKWiU</td> | 
        
          |  | <td>Ilość, jedn.</td> | 
        
          |  | <td>Cena jedn.</td> | 
        
          |  | <td>Wartość</td> | 
        
          |  | </tr> | 
        
          |  | <tr> | 
        
          |  | <td><?php echo $config->item; ?></td> | 
        
          |  | <td><?php echo $config->pkwiu; ?></td> | 
        
          |  | <td>1 szt.</td> | 
        
          |  | <td><?php echo number_format($amount, 2, ',', ' '); ?></td> | 
        
          |  | <td><?php echo number_format($amount, 2, ',', ' '); ?></td> | 
        
          |  | </tr> | 
        
          |  | <tr class="total"> | 
        
          |  | <td colspan="4">RAZEM</td> | 
        
          |  | <td><?php echo number_format($amount, 2, ',', ' '); ?></td> | 
        
          |  | </tr> | 
        
          |  | </table> | 
        
          |  |  | 
        
          |  | <h2>Do zapłaty</h2> | 
        
          |  | <p class="total-amount"><?php echo number_format($amount, 2, ',', ' '); ?> PLN</p> | 
        
          |  | <p class="total-words">Słownie: <?php echo $words_amount; ?> PLN <?php echo round($remainder * 100); ?>/100</p> | 
        
          |  | <p class="payment-method">Sposób zapłaty: Przelew</p> | 
        
          |  | <p class="additional-clauses"><?php echo $config->clauses ?? ''; ?></p> | 
        
          |  |  | 
        
          |  | </body> | 
        
          |  | </html> | 
        
          |  | <?php | 
        
          |  | endif; # isset _POST | 
        
          |  |  | 
        
          |  | function noun_form($no, $one, $two, $many) { | 
        
          |  | return $no === 1 ? $one : ( | 
        
          |  | ($no % 100 < 5 || $no % 100 > 21) && in_array($no % 10, [2, 3, 4]) ? $two : | 
        
          |  | $many | 
        
          |  | ); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | function wording($amount) { | 
        
          |  | $nos = [ | 
        
          |  | 1 => 'jeden', | 
        
          |  | 2 => 'dwa', | 
        
          |  | 3 => 'trzy', | 
        
          |  | 4 => 'cztery', | 
        
          |  | 5 => 'pięć', | 
        
          |  | 6 => 'sześć', | 
        
          |  | 7 => 'siedem', | 
        
          |  | 8 => 'osiem', | 
        
          |  | 9 => 'dziewięć', | 
        
          |  | 10 => 'dziesięć', | 
        
          |  | 11 => 'jedenaście', | 
        
          |  | 12 => 'dwanaście', | 
        
          |  | 13 => 'trzynaście', | 
        
          |  | 14 => 'czternaście', | 
        
          |  | 15 => 'piętnaście', | 
        
          |  | 16 => 'szesnaście', | 
        
          |  | 17 => 'siedemnaście', | 
        
          |  | 18 => 'osiemnaście', | 
        
          |  | 19 => 'dziewiętnaście', | 
        
          |  | 20 => 'dwadzieścia', | 
        
          |  | 30 => 'trzydzieści', | 
        
          |  | 40 => 'czterdzieści', | 
        
          |  | 50 => 'pięćdziesiąt', | 
        
          |  | 60 => 'sześćdziesiąt', | 
        
          |  | 70 => 'siedemdziesiąt', | 
        
          |  | 80 => 'osiemdziesiąt', | 
        
          |  | 90 => 'dziewięćdziesiąt', | 
        
          |  | 100 => 'sto', | 
        
          |  | 200 => 'dwieście', | 
        
          |  | 300 => 'trzysta', | 
        
          |  | 400 => 'czterysta', | 
        
          |  | 500 => 'pięćset', | 
        
          |  | 600 => 'sześćset', | 
        
          |  | 700 => 'siedemset', | 
        
          |  | 800 => 'osiemset', | 
        
          |  | 900 => 'dziewięćset', | 
        
          |  | ]; | 
        
          |  |  | 
        
          |  | $remainder = $amount; | 
        
          |  | $words_amount = ''; | 
        
          |  |  | 
        
          |  | if ($remainder >= 1000000) { | 
        
          |  | $millions = intdiv($remainder, 1000000); | 
        
          |  | if ($millions > 1) $words_amount .= wording($millions)[0]; | 
        
          |  | $words_amount .= noun_form($millions, 'milion ', 'miliony ', 'milionów '); | 
        
          |  | $remainder %= 1000000; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | if ($remainder >= 1000) { | 
        
          |  | $thousands = intdiv($remainder, 1000); | 
        
          |  | if ($thousands > 1) $words_amount .= wording($thousands)[0]; | 
        
          |  | $words_amount .= noun_form($thousands, 'tysiąc ', 'tysiące ', 'tysięcy '); | 
        
          |  | $remainder %= 1000; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | for (; $remainder >= 1;) { | 
        
          |  | $max = array_filter( | 
        
          |  | array_keys($nos), | 
        
          |  | function ($no) use ($remainder) { | 
        
          |  | return $no <= $remainder; | 
        
          |  | } | 
        
          |  | ); | 
        
          |  |  | 
        
          |  | if (count($max) < 1) { | 
        
          |  | break; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | $max = max($max); | 
        
          |  | $words_amount .= $nos[$max] . ' '; | 
        
          |  | $remainder -= $max; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | return [$words_amount, $remainder]; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | if (!file_exists('invoice.css')) { | 
        
          |  | file_put_contents('invoice.css', | 
        
          |  | <<<EOCSS | 
        
          |  | html, body { | 
        
          |  | font-family: sans-serif; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | h2 { | 
        
          |  | font-size: 1em; | 
        
          |  | padding: .5em 0; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | table { | 
        
          |  | width: 100%; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | table td { | 
        
          |  | padding: 0; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | table .header { | 
        
          |  | font-weight: bold; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | table .header td { | 
        
          |  | padding-bottom: .5em; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | table .total { | 
        
          |  | font-weight: bold; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | table .total td { | 
        
          |  | padding-top: .65em; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | p { | 
        
          |  | margin: 0; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | input[type=number]::-webkit-inner-spin-button, | 
        
          |  | input[type=number]::-webkit-outer-spin-button { | 
        
          |  | -webkit-appearance: none; | 
        
          |  | margin: 0; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | input[type=number] { | 
        
          |  | -moz-appearance:textfield; /* Firefox */ | 
        
          |  | } | 
        
          |  |  | 
        
          |  | .total-amount { | 
        
          |  | font-size: 2em; | 
        
          |  | font-weight: bold; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | .total-words, | 
        
          |  | .payment-method, | 
        
          |  | .additional-clauses { | 
        
          |  | margin-top: .5em; | 
        
          |  | } | 
        
          |  | EOCSS | 
        
          |  | ); | 
        
          |  | } |