Spaces:
Running
Running
| import getpass | |
| import hashlib | |
| import json | |
| import os | |
| from datetime import datetime | |
| DATA_FILE = "invoice_data.json" | |
| INVOICE_FOLDER = "invoices" | |
| def hash_password(password: str) -> str: | |
| return hashlib.sha256(password.encode("utf-8")).hexdigest() | |
| def prompt_non_empty(prompt: str) -> str: | |
| while True: | |
| value = input(prompt).strip() | |
| if value: | |
| return value | |
| print("Pole nie moze byc puste.") | |
| def prompt_positive_float(prompt: str) -> float: | |
| while True: | |
| raw_value = input(prompt).replace(",", ".").strip() | |
| try: | |
| value = float(raw_value) | |
| except ValueError: | |
| print("Wprowadz liczbe.") | |
| continue | |
| if value <= 0: | |
| print("Wartosc musi byc wieksza od zera.") | |
| continue | |
| return value | |
| def load_data(): | |
| if not os.path.exists(DATA_FILE): | |
| return None | |
| with open(DATA_FILE, "r", encoding="utf-8") as handle: | |
| return json.load(handle) | |
| def save_data(data) -> None: | |
| with open(DATA_FILE, "w", encoding="utf-8") as handle: | |
| json.dump(data, handle, indent=2, ensure_ascii=False) | |
| def run_setup(): | |
| print("=== Konfiguracja konta przedsiebiorcy ===") | |
| business = { | |
| "company_name": prompt_non_empty("Nazwa firmy: "), | |
| "owner_name": prompt_non_empty("Imie i nazwisko wlasciciela: "), | |
| "address": prompt_non_empty("Adres: "), | |
| "tax_id": prompt_non_empty("NIP: "), | |
| "bank_account": prompt_non_empty("Numer konta bankowego: "), | |
| } | |
| print("\nUstaw haslo do logowania.") | |
| while True: | |
| password = getpass.getpass("Haslo: ") | |
| confirm = getpass.getpass("Powtorz haslo: ") | |
| if not password: | |
| print("Haslo nie moze byc puste.") | |
| continue | |
| if password != confirm: | |
| print("Hasla nie sa identyczne. Sprobuj ponownie.") | |
| continue | |
| break | |
| data = { | |
| "business": business, | |
| "password_hash": hash_password(password), | |
| "invoices": [], | |
| } | |
| save_data(data) | |
| print("\nDane zapisane. Uruchom aplikacje ponownie, aby zalogowac sie i wystawiac faktury.") | |
| def authenticate(data) -> bool: | |
| for attempt in range(3): | |
| password = getpass.getpass("Haslo: ") | |
| if hash_password(password) == data["password_hash"]: | |
| return True | |
| print("Nieprawidlowe haslo.") | |
| return False | |
| def prompt_client_details(): | |
| answer = input("Dodac dane klienta? (T/N): ").strip().lower() | |
| if answer not in ("t", "tak"): | |
| return {} | |
| print("\n=== Dane klienta ===") | |
| client = { | |
| "name": prompt_non_empty("Nazwa / Imie i nazwisko: "), | |
| "address": prompt_non_empty("Adres: "), | |
| "tax_id": input("NIP (opcjonalnie): ").strip(), | |
| } | |
| return client | |
| def format_invoice_text(invoice_id: str, business: dict, invoice: dict) -> str: | |
| lines = [ | |
| f"Faktura: {invoice_id}", | |
| f"Data wystawienia: {invoice['issued_at']}", | |
| "", | |
| "=== Sprzedawca ===", | |
| f"Nazwa: {business['company_name']}", | |
| f"Wlasciciel: {business['owner_name']}", | |
| f"Adres: {business['address']}", | |
| f"NIP: {business['tax_id']}", | |
| f"Konto bankowe: {business['bank_account']}", | |
| "", | |
| "=== Nabywca ===", | |
| ] | |
| client = invoice.get("client", {}) | |
| if client: | |
| lines.extend( | |
| [ | |
| f"Nazwa: {client.get('name', '')}", | |
| f"Adres: {client.get('address', '')}", | |
| f"NIP: {client.get('tax_id', '') or '---'}", | |
| ] | |
| ) | |
| else: | |
| lines.append("Brak danych klienta (pole opcjonalne).") | |
| lines.extend( | |
| [ | |
| "", | |
| "=== Pozycja ===", | |
| f"Opis: {invoice['item_description']}", | |
| f"Ilosc: {invoice['quantity']}", | |
| f"Cena jednostkowa: {invoice['unit_price']:.2f} PLN", | |
| f"Wartosc netto: {invoice['net_total']:.2f} PLN", | |
| ] | |
| ) | |
| return "\n".join(lines) | |
| def create_invoice(data): | |
| print("\n=== Wystaw fakture ===") | |
| description = prompt_non_empty("Opis uslugi / towaru: ") | |
| quantity = prompt_positive_float("Ilosc: ") | |
| unit_price = prompt_positive_float("Cena jednostkowa (PLN): ") | |
| client = prompt_client_details() | |
| issued_at = datetime.now().strftime("%Y-%m-%d %H:%M") | |
| invoice_id = datetime.now().strftime("FV-%Y%m%d-%H%M%S") | |
| net_total = quantity * unit_price | |
| invoice = { | |
| "invoice_id": invoice_id, | |
| "issued_at": issued_at, | |
| "item_description": description, | |
| "quantity": quantity, | |
| "unit_price": unit_price, | |
| "net_total": net_total, | |
| "client": client, | |
| } | |
| os.makedirs(INVOICE_FOLDER, exist_ok=True) | |
| invoice_path = os.path.join(INVOICE_FOLDER, f"{invoice_id}.txt") | |
| with open(invoice_path, "w", encoding="utf-8") as handle: | |
| handle.write(format_invoice_text(invoice_id, data["business"], invoice)) | |
| data["invoices"].append(invoice) | |
| save_data(data) | |
| print(f"\nFaktura zapisana do {invoice_path}") | |
| print(f"Suma do zaplaty: {net_total:.2f} PLN") | |
| def main(): | |
| data = load_data() | |
| if data is None: | |
| run_setup() | |
| return | |
| print("=== Logowanie ===") | |
| if not authenticate(data): | |
| print("Zbyt wiele nieudanych prob logowania. Zakonczono.") | |
| return | |
| create_invoice(data) | |
| if __name__ == "__main__": | |
| main() | |