Add notes

This commit is contained in:
Daniel O'Connell 2019-10-07 18:28:55 +02:00
parent 664098705b
commit b67a6138a0
4 changed files with 86 additions and 41 deletions

View File

@ -59,8 +59,9 @@ The buyer can have the following keys
### Items
The list of items should contain maps with a required :title, optional :vat (if not provided it is assumed that
item is VAT free), :to (the date from which this item is valid), :from (the date till which this item is valid)
and a key providing the cost of the item. The price can be provided in one of the following ways:
item is VAT free), :to (the date from which this item is valid), :from (the date till which this item is valid),
:notes (a list of extra notes to be added at the bottom of the invoice) and a key providing the cost of the item.
The price can be provided in one of the following ways:
* :netto - is a set price and will be displayed as provided
* :hourly - is an hourly price - JIRA will be queried in order to work out how many hours should be billed
@ -80,7 +81,7 @@ and a key providing the cost of the item. The price can be provided in one of th
Examples:
; 8% VAT, and a price of 600, recurring every period before 2019-05-30
{:vat 8 :netto 600 :title "Shoes" :to "2019-05-30"}
{:vat 8 :netto 600 :title "Shoes" :to "2019-05-30" :notes ["A note at the bottom"]}
; 12% VAT, and an hourly rate of 12, first appearing on 2019-07-01
{:vat 12 :hourly 12 :title "Something worth 12/h" :from "2019-07-01"}

View File

@ -12,6 +12,7 @@
:items [{:vat 8 :netto 123.21 :title "Buty kowbojskie"}
{:vat 21 :hourly 43.12 :title "Usługa szewska"}
{:netto 321.45 :title "Usługa szewska bez VAT"}
{:netto 321.45 :title "Usługa szewska zwolniona z VAT" :notes ["Podstawa zwolnienia z VAT: art. 113 ust. 1 i 9 Ustawa o VAT"]}
{:vat 23 :function (* :worked (+ 1 2 3 (- 23 13))) :title "Pucowania obuwia"}
{:vat 23 :base 4300.00 :per-day 4 :title "Praca za ladą"}]
:font-path "/usr/share/fonts/truetype/freefont/FreeSans.ttf"

View File

@ -21,6 +21,15 @@
(-> item vat round str)
(brutto item)])))
(defn format-notes
"Adds an `Uwagi` section with the given notes, one per line."
[notes]
(when (seq notes)
[[:spacer 2] [:line]
(concat
[:table {:border false :padding 0 :spacing 0} [[:phrase {:style :bold} "Uwagi:"]]]
(map vector notes))]))
(defn get-title [team who which]
(let [[nr month year] (-> which (str/split #"/"))
@ -34,51 +43,58 @@
"Generate the actual pdf body"
[title seller buyer items when number font]
[{:title title
:right-margin 50
:author (:name seller)
:bottom-margin 10
:left-margin 10
:top-margin 20
:font font
:size "a4"
:footer "page"}
:right-margin 50
:author (:name seller)
:bottom-margin 10
:left-margin 10
:top-margin 20
:font font
:size "a4"
:footer "page"}
[:heading "Faktura"]
[:spacer]
[:paragraph (str "Nr " number)]
[:spacer 2]
[:heading "Faktura"]
[:spacer]
[:paragraph (str "Nr " number)]
[:spacer 2]
[:table {:border false :padding 0 :spacing 0 :num-cols 6}
[(format-param "sprzedawca") (format-value (:name seller)) (format-param "nabywca") (format-value (:name buyer))]
[(format-param "adres") (format-value (:address seller)) (format-param "adres") (format-value (:address buyer))]
[(format-param "nip") (format-value (:nip seller)) (format-param "nip") (format-value (:nip buyer))]
(clojure.core/when (:phone seller) [(format-param "numer telefonu") (format-value (:phone seller))])]
[:table {:border false :padding 0 :spacing 0 :num-cols 6}
[(format-param "sprzedawca") (format-value (:name seller)) (format-param "nabywca") (format-value (:name buyer))]
[(format-param "adres") (format-value (:address seller)) (format-param "adres") (format-value (:address buyer))]
[(format-param "nip") (format-value (:nip seller)) (format-param "nip") (format-value (:nip buyer))]
(clojure.core/when (:phone seller) [(format-param "numer telefonu") (format-value (:phone seller))])]
[:spacer]
[:line]
[:table {:border false :padding 0 :spacing 0 :num-cols 6}
[(format-param "data wystawienia") (-> when .toString format-value) (format-param "sposób płatności") (format-value "Przelew")]
[(format-param "data sprzedaży") (-> when .toString format-value) (format-param "bank") (format-value (:bank seller))]
[(format-param "termin płatności") (-> when (.plusDays 14) .toString format-value) (format-param "numer konta") (format-value (:account seller))]]
[:spacer]
[:line]
[:table {:border false :padding 0 :spacing 0 :num-cols 6}
[(format-param "data wystawienia") (-> when .toString format-value) (format-param "sposób płatności") (format-value "Przelew")]
[(format-param "data sprzedaży") (-> when .toString format-value) (format-param "bank") (format-value (:bank seller))]
[(format-param "termin płatności") (-> when (.plusDays 14) .toString format-value) (format-param "numer konta") (format-value (:account seller))]]
(concat
[:table
{:header [{:background-color [216 247 249]} "Lp." [:cell {:colspan 4} "Nazwa"] "Ilość" "Cena netto" "Stawka VAT" "Kwota VAT" "Wartość brutto"]
:num-cols 10}]
(->> items
(map format-product)
(map-indexed #(concat [(inc %1)] %2)))
[[[:cell {:background-color [84 219 229] :colspan 5 :align :center} "Razem"]
(format-total items (constantly 1))
(format-total items :netto)
""
(format-total items vat)
(format-total items brutto)]])])
(concat
[:table
{:header [{:background-color [216 247 249]} "Lp." [:cell {:colspan 4} "Nazwa"] "Ilość" "Cena netto" "Stawka VAT" "Kwota VAT" "Wartość brutto"]
:num-cols 10}]
(->> items
(map format-product)
(map-indexed #(concat [(inc %1)] %2)))
[[[:cell {:background-color [84 219 229] :colspan 5 :align :center} "Razem"]
(format-total items (constantly 1))
(format-total items :netto)
""
(format-total items vat)
(format-total items brutto)]])])
(defn add-notes
"Some items require extra notes to be added (for various legal reasons)"
[body items]
(->> items (map :notes) (remove nil?) flatten distinct format-notes (conj body)))
(defn render [seller buyer items when number & [font-path]]
(let [title (get-title (:team seller) (:name seller) number)]
(println " -" title)
(pdf (pdf-body title seller buyer items when number (clojure.core/when font-path{:encoding :unicode :ttf-name font-path}))
(str title ".pdf"))
(-> title
(pdf-body seller buyer items when number (clojure.core/when font-path {:encoding :unicode :ttf-name font-path}))
(add-notes items)
(pdf (str title ".pdf")))
title))

View File

@ -33,6 +33,15 @@
(is (= (format-product {:netto 1000 :title "bla bla"})
[[:cell {:colspan 4} "bla bla"] "1" "1000.0" "zw." "0.0" "1000.0"]))))
(deftest test-format-notes
(testing "Check whether notes get correctly formatted"
(is (= (format-notes ["line 1" "line 2" "line 3"])
[[:spacer 2] [:line] (list :table {:border false, :padding 0, :spacing 0} [[:phrase {:style :bold} "Uwagi:"]] ["line 1"] ["line 2"] ["line 3"])])))
(testing "Check whether the notes section is skipped if none provided."
(is (nil? (format-notes [])))
(is (nil? (format-notes nil)))))
(deftest test-get-title
(testing "Check whether getting titles works"
(is (= (get-title nil "mr blobby" "2019/02/11") "mr_blobby_luty_2019_02_11"))
@ -106,3 +115,21 @@
""
[:cell {:background-color [216 247 249]} "9.86"]
[:cell {:background-color [216 247 249]} "454.52"]])])))))
(deftest test-add-notes
(testing "Check whether notes get correctly added"
(is (= (add-notes [:table] [{:notes ["line 1" "line 2"]} {} {} {:notes ["line 3"]} {:notes []}])
[:table
[[:spacer 2] [:line]
(list :table {:border false, :padding 0, :spacing 0} [[:phrase {:style :bold} "Uwagi:"]]
["line 1"] ["line 2"] ["line 3"])]])))
(testing "Check whether duplicates get removed"
(is (= (add-notes [:table] [{:notes ["line 1" "line 1"]} {} {} {:notes ["line 1"]} {:notes []}])
[:table
[[:spacer 2] [:line]
(list :table {:border false, :padding 0, :spacing 0} [[:phrase {:style :bold} "Uwagi:"]] ["line 1"])]])))
(testing "Check whether the notes section is skipped if none provided."
(is (= (add-notes [:table] []) [:table nil]))
(is (= (add-notes [:table] nil) [:table nil]))))