Parcourir la source

Refactor wrt my needs

master
Peter Hatina il y a 2 ans
Parent
révision
631577bba9
5 fichiers modifiés avec 147 ajouts et 57 suppressions
  1. +3
    -1
      .gitignore
  2. +7
    -3
      lib/invoice/cli.py
  3. +13
    -5
      lib/invoice/db/companies.py
  4. +41
    -11
      lib/invoice/db/invoices.py
  5. +83
    -37
      templates/invoice.tex

+ 3
- 1
.gitignore Voir le fichier

@@ -1,3 +1,5 @@
__pycache__
*.*~
*.pyc
*.pyo
*.sw[op]
__pycache__

+ 7
- 3
lib/invoice/cli.py Voir le fichier

@@ -3,6 +3,7 @@
from __future__ import print_function

import os, sys, argparse, datetime, subprocess
import locale
import logging
logging.basicConfig(level=logging.DEBUG)
log = logging.getLogger()
@@ -179,7 +180,10 @@ class Application:
log.debug("Creating TeX invoice...")
self._check_path(self.tmp_path)
result = tempita.Template(open(tex_template).read()).substitute(
invoice=invoice_data, issuer=issuer_data, customer=customer_data)
invoice=invoice_data,
issuer=issuer_data,
customer=customer_data,
eur=lambda x: '{:20,.2f} EUR'.format(x).replace(',', '\\,').replace('.', ','))
open(tex_file, "w").write(str(result))
assert(os.path.exists(tex_file))

@@ -193,8 +197,8 @@ class Application:
os.rename(tmp_pdf_file, pdf_file)

assert(os.path.exists(pdf_file))
log.debug("Running PDF viewer...")
subprocess.call((self.pdf_program, pdf_file))
#log.debug("Running PDF viewer...")
#subprocess.call((self.pdf_program, pdf_file))

def _check_path(self, path):
if not os.path.exists(path):


+ 13
- 5
lib/invoice/db/companies.py Voir le fichier

@@ -14,9 +14,13 @@ class Companies(List):

Name -- full company name
Address -- company address, repeat to get multiple lines
Number -- identification number
ICO -- identification number
DIC -- tax identification number
ICDPH
IBAN
SWIFT
Comment -- additional information that you want to see on the invoice
"""
"""
_directory = "companies"
_regex = re.compile("^(?P<name>[a-z0-9-]+)$")
_template = "{name}"
@@ -24,7 +28,11 @@ class Companies(List):
Name:
Address:
Address:
Number:
ICO:
DIC:
ICDPH:
IBAN:
SWIFT:
"""
def _item_class(self):
return Company
@@ -34,9 +42,9 @@ class Company(Item):
return CompanyData

class CompanyData(Data):
_fields = ["name", "number", "ic", "bank_account"]
_fields = ["name", "ico", "dic", "icdph", "iban", "swift"]
_multivalue_fields = ["address", "comment"]

def _postprocess(self):
self.rename_key("ic", "number")
#self.rename_key("ic", "number")
self.rename_key("comment", "comments")

+ 41
- 11
lib/invoice/db/invoices.py Voir le fichier

@@ -13,12 +13,19 @@ class Invoices(List):
When editing data files, you can use the following
directives:

Item -- price, followed by ':', optional whitespace and item description
Item -- Description|Amount|Unit|Rate
Issued -- Date when the invoice was issued in YYYY-MM-DD format
Due -- Due date YYYY-MM-DD
Delivered -- Date when the services were delivered in YYYY-MM-DD format
Note -- a note at the and of the invoice
Advance -- amount of money already paid
"""
data_template = """\
Item:
Item:Description|Amount|Unit|Rate
Issued:
Delivered:
Due:
Advance: 0
"""
_directory = "income"
_regex = re.compile("^(?P<date>[0-9]{8})-(?P<number>[0-9]{3})-(?P<company_name>[a-z0-9-]+)$")
@@ -59,10 +66,10 @@ class Invoice(Item):
self._selector["number"] = int(self.number)

class InvoiceData(Data):
_fields = ["due", "paid", "payment"]
_fields = ["issued", "due", "delivered", "paid", "payment", "advance"]
_multivalue_fields = ["item", "address", "note"]
_date_regex = re.compile(r"^(\d{4})-?(\d{2})-?(\d{2})$")
_item_regex = re.compile(r"^(-?\d+)[:;]\s*(.*)$")
_item_regex = re.compile(r"^([^|]*)\|(-?\d+)\|([^|]*)\|(-?\d+)$")
_number_template = "{year}{number:03}"

def _parse_date(self, date):
@@ -90,14 +97,33 @@ class InvoiceData(Data):
match = self._item_regex.match(item)
if not match:
raise ValueError("Bad item format: {0}".format(item))
price, description = match.groups()
items.append((description, int(price)))
description, amount, unit, rate = match.groups()
items.append((description, float(amount), unit, float(rate)))
del self._data["item"]
m_advance = float(self.advance or 0)
m_sum = sum(item[1] * item[3] for item in items)
self._data["items"] = items
self._data["sum"] = sum(item[1] for item in items)
self._data["sum"] = m_sum
self._data["advance"] = m_advance
self._data["topay"] = m_sum - m_advance

def _postprocess_dates(self):
date = self._parse_date(self._item.date)
if self.issued:
try:
issued = self._parse_date(self.issued)
except ValueError:
raise ValueError("Bad issued format: {0}".format(self.issued))
else:
issued = datetime.datetime.now()

if self.delivered:
try:
delivered = self._parse_date(self.delivered)
except ValueError:
raise ValueError("Bad delivered format: {0}".format(self.delivered))
else:
delivered = datetime.datetime.now()

if self.due:
try:
due = self._parse_date(self.due)
@@ -108,8 +134,12 @@ class InvoiceData(Data):
raise ValueError("Bad due format: {0}".format(self.due))
else:
due = datetime.timedelta(14)
log.debug("Due: {0}".format(due))
log.debug("Issued : {0}".format(issued))
log.debug("Delivered: {0}".format(delivered))
log.debug("Due : {0}".format(due))
if isinstance(due, datetime.timedelta):
due += date
self._data["date"] = date
due += issued

self._data["issued"] = issued
self._data["delivered"] = delivered
self._data["due"] = due

+ 83
- 37
templates/invoice.tex Voir le fichier

@@ -1,10 +1,11 @@
\documentclass[10pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[left=0.5cm,right=0.5cm,top=0.5cm,bottom=0.5cm,noheadfoot,nomarginpar,showframe]{geometry}
\usepackage{lmodern}
%\usepackage[czech]{babel}
\usepackage{a4wide}
\usepackage{tabularx}
\usepackage{tabu}
\usepackage{multirow}
\usepackage{graphicx}
\renewcommand{\familydefault}{\sfdefault}
\setlength{\extrarowheight}{3pt}
@@ -12,16 +13,19 @@
\begin{document}
\footnotesize

\pagestyle{empty}

\begin{center}
\begin{tabularx}{\textwidth}{|XXXX|}
\cline{3-4}
\multicolumn{2}{X}{} & \multicolumn{2}{|X|}{} \\
\multicolumn{2}{X}{} & \multicolumn{2}{|l|}{\large Faktura: \hfill {{invoice.number}}} \\
\multicolumn{2}{X}{} & \multicolumn{2}{|X|}{} \\
\hline
& & & \\
\bf Dodavatel: & & \bf Odběratel & \\[1em]
\multicolumn{2}{|l}{\large\bf {{issuer.name}}} & \multicolumn{2}{l|}{\large\bf {{customer.name}}} \\
\begin{tabularx}{\textwidth}{XXXX}

&&& \\
\multicolumn{4}{l}{\bf \large Faktúra: \hfill {{invoice.number}}} \\
&&& \\ \hline
\multicolumn{2}{X|}{} & \multicolumn{2}{X}{} \\
\multicolumn{2}{l|}{\bf Dodávateľ:} & \multicolumn{2}{X}{\bf Odberateľ} \\
\multicolumn{2}{X|}{} & \multicolumn{2}{X}{} \\

\multicolumn{2}{l|}{\large\bf {{issuer.name}}} & \multicolumn{2}{l}{\large\bf {{customer.name}}} \\
{{py:
a1 = issuer.address[:]
a2 = customer.address[:]
@@ -29,13 +33,29 @@ a1 += (len(a2)-len(a1))*[""]
a2 += (len(a1)-len(a2))*[""]
}}
{{for f1, f2 in zip(a1, a2)}}
\multicolumn{2}{|l}{\large {{f1}}} & \multicolumn{2}{l|}{\large {{f2}}} \\
\multicolumn{2}{l|}{\large {{f1}}} & \multicolumn{2}{l}{\large {{f2}}} \\
{{endfor}}

& & & \\
\multicolumn{2}{|l}{IČ: {{issuer.number}}} & \multicolumn{2}{l|}{IČ: {{customer.number}}} \\
& & & \\
% COMPANY ID NUMBERS
\multicolumn{2}{X|}{} & \multicolumn{2}{X}{} \\
\multicolumn{2}{l|}{IČO: {{issuer.ico}}} & \multicolumn{2}{l}{IČO: {{if customer.icdph}}\hspace{0.3cm}{{endif}} {{customer.ico}}} \\
\multicolumn{2}{l|}{DIČ: {{issuer.dic}}} & \multicolumn{2}{l}{DIČ: {{if customer.icdph}}\hspace{0.3cm}{{endif}} {{customer.dic}}} \\
{{if issuer.icdph or customer.icdph}}
{{if issuer.icdph}}
\multicolumn{2}{l|}{IČDPH: {{issuer.icdph}}} &
{{else}}
\multicolumn{2}{l|}{Neplatca DPH} &
{{endif}}
{{if customer.icdph}}
\multicolumn{2}{l}{IČDPH: {{customer.icdph}}} \\
{{else}}
\multicolumn{2}{l}{} \\
{{endif}}
{{endif}}

\multicolumn{2}{X|}{} & \multicolumn{2}{X}{} \\

% Comments
{{py:
a1 = issuer.comments[:]
a2 = customer.comments[:]
@@ -43,45 +63,71 @@ a1 += (len(a2)-len(a1))*[""]
a2 += (len(a1)-len(a2))*[""]
}}
{{for f1, f2 in zip(a1, a2)}}
\multicolumn{2}{|l}{ {{f1}}} & \multicolumn{2}{l|}{ {{f2}}} \\
\multicolumn{2}{l|}{ \footnotesize {{f1}} } & \multicolumn{2}{l}{ \footnotesize {{f2}} } \\
{{endfor}}
\multicolumn{2}{X|}{} & \multicolumn{2}{X}{} \\

& & & \\
\hline
& & & \\
\bf Platební podmínky: & & & \\[1em]
\large Forma úhrady: & \large {{"hotově" if invoice.payment=="cash" else "převodem"}} & \large Datum vystavení: & \multicolumn{1}{r|}{\large {{invoice.date.strftime("%d.%m.%Y")}}} \\
\large Číslo účtu: & \large {{issuer.bank_account}} & \multicolumn{2}{l|}{\large\bf Datum splatnosti: \hfill {{invoice.due.strftime("%d.%m.%Y")}}} \\
\large Variabilní symbol: & \large {{invoice.number}} & & \\

\multicolumn{2}{l|}{\bf Platobné podmienky:} & & \\
Forma úhrady: & \multicolumn{1}{l|}{ {{"v~hotovosti" if invoice.payment=="cash" else "prevodom"}}} & & \\
IBAN: & \multicolumn{1}{l|}{ {{issuer.iban}}} & Dátum vystavenia: & \multicolumn{1}{r}{ {{invoice.issued.strftime("%d.%m.%Y")}}} \\
SWIFT/BIC: & \multicolumn{1}{l|}{ {{issuer.swift}} } & \multicolumn{2}{l}{Dátum dodania: \hfill {{invoice.delivered.strftime("%d.%m.%Y")}}} \\
Variabilný symbol: & \multicolumn{1}{l|}{ {{invoice.number}}} & \multicolumn{2}{l}{\large\bf Dátum splatnosti: \hfill {{invoice.due.strftime("%d.%m.%Y")}}} \\
% COMMENTS
{{if invoice.notes}}
& & & \\
\hline
\multicolumn{2}{X|}{} & \multicolumn{2}{X}{} \\ \hline
& & & \\
\bf Poznámky: & & & \\[1em]
{{for note in invoice.notes}}
\multicolumn{4}{|l|}{\large {{note}}} \\
\multicolumn{4}{l}{\large {{note}}} \\
{{endfor}}
{{endif}}

\multicolumn{2}{X|}{} & \multicolumn{2}{X}{} \\ \hline

& & & \\
\hline
& & & \\
\bf Fakturujeme vám: & & & \\[1em]

{{for item in invoice.items}}
\multicolumn{4}{|l|}{\normalsize {{item[0]}} \hfill {{item[1]}} Kč} \\
{{endfor}}
\multicolumn{4}{l}{\bf Faktúrujeme Vám za služby podľa Rámcovej zmluvy} \\

& & & \\
\hline
& & & \\
\multicolumn{3}{|l}{\large\bf Celkem k úhradě:} & \multicolumn{1}{r|}{\large\bf {{invoice.sum}} Kč} \\

% ITEMS
\multicolumn{4}{X}{%
\hspace*{-1.35\tabcolsep}
\begin{tabu} to \textwidth {%
>{\hsize=2.5\hsize}X[l]%
>{\hsize=0.5\hsize}X[r]%
>{\hsize=0.5\hsize}X[r]%
>{\hsize=0.5\hsize}X[r]%
>{\hsize=0.5\hsize}X[r]}
\normalsize \bf Popis položky & \bf Množstvo & \bf MJ & \bf Cena za MJ & \bf Celková cena \\
{{for item in invoice.items}}
{{item[0]}} & {{item[1]}} & {{item[2]}} & {{eur(item[3])}} & {{eur(item[1] * item[3])}} \\
{{endfor}}
\end{tabu}
} \\

%& & & \\
%\hline
%& & & \\
%\multicolumn{3}{l}{\large\bf Celkom k úhrade:} & \multicolumn{1}{r}{\large\bf {{eur(invoice.sum)}} } \\
& & & \\
\hline
\multicolumn{4}{r}{} \\
\multicolumn{4}{r}{\includegraphics[scale=1]{../signature.png}\hspace{.4cm} } \\
%\multicolumn{4}{r}{\includegraphics[scale=1]{../signature.jpg}\hspace{.4cm} } \\
\end{tabularx}
\end{center}

\vfill

% Footer
\centering
\begin{tabu} to \textwidth {XX|XX|X|X[r]}
\hline
Vyhotovil: Peter Hatina && Prevzal: && Celková suma: & {{eur(invoice.sum)}} \\ \cline{5-6}
\multirow{2}{*}{\hspace*{1.0cm}\includegraphics[scale=0.3]{../signature.jpg} } && && Uhradené zálohami: & {{eur(invoice.advance)}} \\ \cline{5-6}
&& && Zostáva uhradiť: & {{eur(invoice.topay)}} \\ \cline{5-6}
&& && & \\
&& && \multirow{-2}{*}{K úhrade:} & \multirow{-2}{*}{ \bf \large {{eur(invoice.topay)}}\phantom} \\
\end{tabu}

\end{document}

Chargement…
Annuler
Enregistrer