-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinvoice_spec.clj
More file actions
94 lines (80 loc) · 3.09 KB
/
invoice_spec.clj
File metadata and controls
94 lines (80 loc) · 3.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
(ns invoice-spec
(:require
[clojure.data.json :as json]
[clojure.spec.alpha :as s])
(:import
(java.text SimpleDateFormat)))
(defn not-blank? [value] (-> value clojure.string/blank? not))
(defn non-empty-string? [x] (and (string? x) (not-blank? x)))
(s/def :customer/name non-empty-string?)
(s/def :customer/email non-empty-string?)
(s/def :invoice/customer (s/keys :req [:customer/name
:customer/email]))
(s/def :tax/rate double?)
(s/def :tax/category #{:iva})
(s/def ::tax (s/keys :req [:tax/category
:tax/rate]))
(s/def :invoice-item/taxes (s/coll-of ::tax :kind vector? :min-count 1))
(s/def :invoice-item/price double?)
(s/def :invoice-item/quantity double?)
(s/def :invoice-item/sku non-empty-string?)
(s/def ::invoice-item
(s/keys :req [:invoice-item/price
:invoice-item/quantity
:invoice-item/sku
:invoice-item/taxes]))
(s/def :invoice/issue-date inst?)
(s/def :invoice/items (s/coll-of ::invoice-item :kind vector? :min-count 1))
(s/def ::invoice
(s/keys :req [:invoice/issue-date
:invoice/customer
:invoice/items]))
;^; Problem 2 Solution
(defn parse-date
"Parse a date string into a date object"
[date-string]
(let [date-formatter (SimpleDateFormat. "dd/MM/yyyy")]
(.parse date-formatter date-string)))
(defn read-json-file
"Read a JSON file and return a map"
[file-name]
(json/read-str (slurp file-name) :key-fn keyword))
(defn get-issue-date
"Get the issue date from the invoice"
[invoice]
(parse-date (get-in invoice [:invoice :issue_date])))
(defn get-customer
"Get the customer from the invoice"
[invoice]
(let [customer (get-in invoice [:invoice :customer])]
{:customer/name (get-in customer [:company_name])
:customer/email (get-in customer [:email])}))
(defn get-items
"Get the items from the invoice"
[invoice]
(let [items (get-in invoice [:invoice :items])]
(map (fn [item]
{:invoice-item/price (get-in item [:price])
:invoice-item/quantity (get-in item [:quantity])
:invoice-item/sku (get-in item [:sku])
:invoice-item/taxes (vec (map (fn [tax]
{
:tax/category (keyword (clojure.string/lower-case (get-in tax [:tax_category])))
:tax/rate (double (get-in tax [:tax_rate]))
})
(get-in item [:taxes])))
})
items)))
(defn generate-invoice
"Generate an invoice that passes the corresponding spec"
[file-name]
(let [invoice (read-json-file file-name)]
{:invoice/issue-date (get-issue-date invoice)
:invoice/customer (get-customer invoice)
:invoice/items (vec (get-items invoice))}))
(defn -main
"Use this execute the invoice spec"
[& args]
(let [invoice (generate-invoice "invoice.json")]
(println (s/valid? ::invoice invoice))
(s/explain ::invoice invoice)))