diff --git a/README.md b/README.md index b719a7b..02aad4b 100644 --- a/README.md +++ b/README.md @@ -55,8 +55,8 @@ The buyer can have the following keys ### Items -The list of items should contain maps with two required keys (:vat and :title), and a key -providing the cost of the item. The price can be provided in one of three ways: +The list of items should contain maps with a required :title, an optional :vat (if not provided it is assumed that +item is VAT free), 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 @@ -69,6 +69,10 @@ providing the cost of the item. The price can be provided in one of three ways: be :base. Otherwise the final price will be scaled accordingly. This is pretty much equivalent to working out what the hourly rate should be in a given month and multiplying it by the number of hours worked in that month + * :function - an S-expression describing how to calculate the net value. Only numbers, basic mathematical + operations (+, -, /, *) and timesheet specific variables are supported (:worked, :required, + :to, :from). + Examples: @@ -81,6 +85,9 @@ Examples: ; 23% VAT, working part time with a base salary of 5000 {:vat 23 :base 5000 :per-day 4 :title "Part time job at 5000"}] + ; 23% VAT, with a custom function + {:vat 23 :function (* :worked (/ 10000 :required)) :title "Custom function"}] + ### Credentials diff --git a/resources/config.edn b/resources/config.edn index 4933f07..6cd1748 100644 --- a/resources/config.edn +++ b/resources/config.edn @@ -9,6 +9,8 @@ :nip 9875645342} :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"} + {: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" :credentials {:tempo-token "5zq7zF9LADefEGAs12eDDas3FDttiM" diff --git a/src/invoices/core.clj b/src/invoices/core.clj index d54c06f..ad95d61 100644 --- a/src/invoices/core.clj +++ b/src/invoices/core.clj @@ -9,25 +9,36 @@ (defn invoice-number [when number] (->> [(or number 1) (-> when .getMonthValue) (-> when .getYear)] (map str) (str/join "/"))) -(defn calc-part-time [when creds {base :base per-day :per-day}] - (let [{worked :worked total :required} (prev-timesheet when creds) - hourly (/ (* base 8) (* total per-day))] +(defn parse-custom [work-log func] + (cond + (and (list? func) (some #{(first func)} '(+ - * /))) (apply (resolve (first func)) + (map (partial parse-custom work-log) (rest func))) + (list? func) (throw (IllegalArgumentException. (str "Invalid functor provided: " (first func)))) + (some #{func} '(:worked :required :to :from)) (func work-log) + :else func)) + +(defn calc-part-time [{worked :worked total :required} {base :base per-day :per-day}] + (let [hourly (/ (* base 8) (* total per-day))] (float (* hourly worked)))) -(defn calc-hourly [when creds {hourly :hourly}] - (-> (prev-timesheet when creds) :worked (* hourly))) +(defn calc-hourly [{worked :worked} {hourly :hourly}] + (* worked hourly)) + +(defn calc-custom [worked {function :function}] + (parse-custom worked function)) -(defn set-price [when creds item] +(defn set-price [worked item] (cond - (contains? item :hourly) (assoc item :netto (calc-hourly when creds item)) - (contains? item :base) (assoc item :netto (calc-part-time when creds item)) + (contains? item :function) (assoc item :netto (calc-custom worked item)) + (contains? item :hourly) (assoc item :netto (calc-hourly worked item)) + (contains? item :base) (assoc item :netto (calc-part-time worked item)) (not (contains? item :netto)) (assoc item :netto 0) :else item)) (defn for-month [when {seller :seller buyer :buyer items :items creds :credentials font-path :font-path} & [number]] (pdf/render seller buyer - (map (partial set-price when creds) items) + (map (partial set-price (prev-timesheet when creds)) items) (pdf/last-working-day when) (invoice-number when number) font-path)) diff --git a/src/invoices/pdf.clj b/src/invoices/pdf.clj index 429bdd6..6758c86 100644 --- a/src/invoices/pdf.clj +++ b/src/invoices/pdf.clj @@ -8,7 +8,7 @@ (float (/ (Math/round (* val 100.0)) 100))) (defn vat [{netto :netto vat-level :vat}] - (round (* netto (/ vat-level 100)))) + (if-not vat-level 0 (* netto (/ vat-level 100)))) (defn brutto [{netto :netto :as item}] (round (+ netto (vat item)))) @@ -22,7 +22,11 @@ (defn format-product [{netto :netto vat-level :vat title :title :as item}] (concat [[:cell {:colspan 4} title]] - (map str [1 (-> netto round str) (str vat-level "%") (vat item) (brutto item)]))) + (map str [1 + (-> netto round str) + (if-not vat-level "zw." (str vat-level "%")) + (-> item vat round str) + (brutto item)]))) (defn get-title [team who which]