|
<?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 |
|
); |
|
} |