diff --git a/resources/public/img/logo-big.png b/resources/public/img/logo-big.png new file mode 100644 index 00000000..01f336f4 Binary files /dev/null and b/resources/public/img/logo-big.png differ diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 49d6f4dd..70525799 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -460,6 +460,12 @@ :start {:type 'Int} :end {:type 'Int}}} + :expected_deposit_page {:fields {:expected_deposits {:type '(list :expected_deposit)} + :count {:type 'Int} + :total {:type 'Int} + :start {:type 'Int} + :end {:type 'Int}}} + :reminder_page {:fields {:reminders {:type '(list :reminder)} :count {:type 'Int} :total {:type 'Int} @@ -631,6 +637,17 @@ :resolve :get-sales-order-page} + :expected_deposit_page {:type :expected_deposit_page + :args {:client_id {:type :id} + :date_range {:type :date_range} + :total_lte {:type :money} + :total_gte {:type :money} + :start {:type 'Int} + :per_page {:type 'Int} + :sort {:type '(list :sort_item)}} + + :resolve :get-expected-deposit-page} + :payment_page {:type '(list :payment_page) :args {:client_id {:type :id} :vendor_id {:type :id} @@ -1268,6 +1285,7 @@ :get-yodlee-provider-account-page gq-yodlee2/get-yodlee-provider-account-page :get-all-payments get-all-payments :get-all-expected-deposits gq-expected-deposit/get-all-expected-deposits + :get-expected-deposit-page gq-expected-deposit/get-expected-deposit-page :get-all-sales-orders get-all-sales-orders :get-payment-page gq-checks/get-payment-page :get-potential-payments gq-checks/get-potential-payments diff --git a/src/clj/auto_ap/graphql/expected_deposit.clj b/src/clj/auto_ap/graphql/expected_deposit.clj index e2f45ac1..7edd216f 100644 --- a/src/clj/auto_ap/graphql/expected_deposit.clj +++ b/src/clj/auto_ap/graphql/expected_deposit.clj @@ -14,3 +14,8 @@ (map ->graphql (first (d-expected-deposit/get-graphql (assoc (<-graphql args) :count Integer/MAX_VALUE))))) + +(defn get-expected-deposit-page [context args value] + (let [args (assoc args :id (:id context)) + [expected-deposits expected-deposit-count] (d-expected-deposit/get-graphql (<-graphql args))] + (result->page expected-deposits expected-deposit-count :expected_deposits args ))) diff --git a/src/clj/auto_ap/handler.clj b/src/clj/auto_ap/handler.clj index 3deb281b..4f43c6b8 100644 --- a/src/clj/auto_ap/handler.clj +++ b/src/clj/auto_ap/handler.clj @@ -57,7 +57,7 @@ exports/export-routes yodlee/routes yodlee2/routes - queries/query-routes + queries/query2-routes invoices/routes graphql/routes auth/routes diff --git a/src/clj/auto_ap/routes/queries.clj b/src/clj/auto_ap/routes/queries.clj index bd8a748f..8d83388a 100644 --- a/src/clj/auto_ap/routes/queries.clj +++ b/src/clj/auto_ap/routes/queries.clj @@ -51,68 +51,82 @@ (into (list) (apply d/q (clojure.edn/read-string query-string) (into [(d/db conn)] (clojure.edn/read-string (get query-params "args" "[]"))))))))) -(def query-routes + +(def json-routes + (context "/queries" [] + (POST "/" {:keys [query-params identity] :as request} + (assert-admin identity) + (let [uuid (str (UUID/randomUUID)) + body (body-string request)] + (s3/put-object :bucket-name (:data-bucket env) + :key (str "queries/" uuid) + :input-stream (io/make-input-stream (.getBytes body) {}) + :metadata {:content-type "application/text" + :user-metadata {:note (query-params "note")}}) + {:body {:query body + :id uuid + :results-url (str "/api/queries/" uuid "/results") + :csv-results-url (str "/api/queries/" uuid "/results/csv") + :json-results-url (str "/api/queries/" uuid "/results/json")}}) + + ) + (PUT "/:query-id" {:keys [query-params identity params] :as request} + (assert-admin identity) + (log/info "Note" (query-params "note")) + (let [body (body-string request)] + (s3/put-object :bucket-name (:data-bucket env) + :key (str "queries/" (:query-id params)) + :input-stream (io/make-input-stream (.getBytes body) {}) + :metadata {:content-type "application/text" + :user-metadata {:note (query-params "note")}}) + {:body {:query body + :id (:query-id params) + :csv-results-url (str "/api/queries/" (:query-id params) "/results/csv") + :json-results-url (str "/api/queries/" (:query-id params) "/results/json")}})) + (GET "/:query-id" {:keys [query-params identity params]} + (assert-admin identity) + (let [{:keys [query-id]} params + obj (s3/get-object :bucket-name (:data-bucket env) + :key (str "queries/" query-id)) + query-string (str (slurp (:object-content obj)))] + (log/info obj) + {:body {:query query-string + :note (:note (:user-metadata (:object-metadata obj))) + :id query-id + :csv-results-url (str "/api/queries/" query-id "/results/csv") + :json-results-url (str "/api/queries/" query-id "/results/json")}})) + + + (GET "/" {:keys [query-params identity params]} + (assert-admin identity) + (let [{:keys [query-id]} params + obj (s3/list-objects :bucket-name (:data-bucket env) + :prefix (str "queries/"))] + (log/info obj) + {:body {}})) + + (GET "/:query-id/results/json" {:keys [query-params identity params]} + {:body (execute-query query-params params)}) + )) + + +(def raw-routes + (context "/queries" [] + (GET "/:query-id/raw" {:keys [query-params identity params]} + (assert-admin identity) + (let [{:keys [query-id]} params + obj (s3/get-object :bucket-name (:data-bucket env) + :key (str "queries/" query-id)) + query-string (str (slurp (:object-content obj)))] + (log/info obj) + {:body query-string})))) + +(def csv-routes + (context "/queries" [] + (GET "/:query-id/results/csv" {:keys [query-params identity params]} + {:body (execute-query query-params params)}))) +(defroutes query2-routes (routes - (wrap-json-response (POST "/queries" {:keys [query-params identity] :as request} - (assert-admin identity) - (let [uuid (str (UUID/randomUUID)) - body (body-string request)] - (s3/put-object :bucket-name (:data-bucket env) - :key (str "queries/" uuid) - :input-stream (io/make-input-stream (.getBytes body) {}) - :metadata {:content-type "application/text" - :user-metadata {:note (query-params "note")}}) - {:body {:query body - :id uuid - :results-url (str "/api/queries/" uuid "/results") - :csv-results-url (str "/api/queries/" uuid "/results/csv") - :json-results-url (str "/api/queries/" uuid "/results/json")}}) - - )) - (wrap-json-response (PUT "/queries/:query-id" {:keys [query-params identity params] :as request} - (assert-admin identity) - (log/info "Note" (query-params "note")) - (let [body (body-string request)] - (s3/put-object :bucket-name (:data-bucket env) - :key (str "queries/" (:query-id params)) - :input-stream (io/make-input-stream (.getBytes body) {}) - :metadata {:content-type "application/text" - :user-metadata {:note (query-params "note")}}) - {:body {:query body - :id (:query-id params) - :csv-results-url (str "/api/queries/" (:query-id params) "/results/csv") - :json-results-url (str "/api/queries/" (:query-id params) "/results/json")}}))) - (wrap-json-response (GET "/queries/:query-id" {:keys [query-params identity params]} - (assert-admin identity) - (let [{:keys [query-id]} params - obj (s3/get-object :bucket-name (:data-bucket env) - :key (str "queries/" query-id)) - query-string (str (slurp (:object-content obj)))] - (log/info obj) - {:body {:query query-string - :note (:note (:user-metadata (:object-metadata obj))) - :id query-id - :csv-results-url (str "/api/queries/" query-id "/results/csv") - :json-results-url (str "/api/queries/" query-id "/results/json")}}))) - (GET "/queries/:query-id/raw" {:keys [query-params identity params]} - (assert-admin identity) - (let [{:keys [query-id]} params - obj (s3/get-object :bucket-name (:data-bucket env) - :key (str "queries/" query-id)) - query-string (str (slurp (:object-content obj)))] - (log/info obj) - {:body query-string})) - - (wrap-json-response (GET "/queries/" {:keys [query-params identity params]} - (assert-admin identity) - (let [{:keys [query-id]} params - obj (s3/list-objects :bucket-name (:data-bucket env) - :prefix (str "queries/"))] - (log/info obj) - {:body {}}))) - - (wrap-json-response (GET "/queries/:query-id/results/json" {:keys [query-params identity params]} - {:body (execute-query query-params params)})) - (wrap-csv-response (GET "/queries/:query-id/results/csv" {:keys [query-params identity params]} - {:body (execute-query query-params params)})) - )) + (wrap-routes json-routes + wrap-json-response) + (wrap-routes csv-routes wrap-csv-response))) diff --git a/src/clj/auto_ap/square/core.clj b/src/clj/auto_ap/square/core.clj index 4a857f46..036f401e 100644 --- a/src/clj/auto_ap/square/core.clj +++ b/src/clj/auto_ap/square/core.clj @@ -23,7 +23,7 @@ (defn fetch-catalog [i] (if i - (do + (try (log/info "looking up catalog for" (str "https://connect.squareup.com/v2/catalog/object/" i)) (->> (client/get (str "https://connect.squareup.com/v2/catalog/object/" i) {:headers {"Square-Version" "2020-08-12" @@ -32,7 +32,10 @@ :query-params {"include_related_items" "true"} :as :json}) :body - :object)) + :object) + (catch Exception e + (log/error e) + nil)) (log/warn "Trying to look up non existant "))) (def fetch-catalog-fast (memoize fetch-catalog)) diff --git a/src/cljc/auto_ap/client_routes.cljc b/src/cljc/auto_ap/client_routes.cljc index 5c196cd6..0ff3b14f 100644 --- a/src/cljc/auto_ap/client_routes.cljc +++ b/src/cljc/auto_ap/client_routes.cljc @@ -22,7 +22,8 @@ "paid" :paid-invoices "voided" :voided-invoices "new" :new-invoice} - "sales-orders/" {"" :sales-orders} + "pos/" {"sales-orders" :sales-orders + "expected-deposits" :expected-deposits} "transactions/" {"" :transactions "unapproved" :unapproved-transactions "approved" :approved-transactions diff --git a/src/cljs/auto_ap/views/components/layouts.cljs b/src/cljs/auto_ap/views/components/layouts.cljs index 2f8c3cb8..ae89f6f5 100644 --- a/src/cljs/auto_ap/views/components/layouts.cljs +++ b/src/cljs/auto_ap/views/components/layouts.cljs @@ -112,7 +112,7 @@ (when (= "admin" (:user/role @user)) [:a.navbar-item {:class [(active-when ap = :sales-orders)] :href (bidi/path-for routes/routes :sales-orders)} - "Sales Orders" ]) + "POS" ]) [:a.navbar-item {:class [(active-when ap = :transactions)] :href (bidi/path-for routes/routes :transactions)} "Transactions" ] diff --git a/src/cljs/auto_ap/views/main.cljs b/src/cljs/auto_ap/views/main.cljs index 3637815a..0a4b3855 100644 --- a/src/cljs/auto_ap/views/main.cljs +++ b/src/cljs/auto_ap/views/main.cljs @@ -19,7 +19,8 @@ [auto-ap.views.pages.ledger.profit-and-loss :refer [profit-and-loss-page]] [auto-ap.views.pages.login :refer [login-page]] [auto-ap.views.pages.payments :refer [payments-page]] - [auto-ap.views.pages.sales-orders :refer [sales-orders-page]] + [auto-ap.views.pages.pos.sales-orders :refer [sales-orders-page]] + [auto-ap.views.pages.pos.expected-deposits :refer [expected-deposits-page]] [auto-ap.views.pages.admin :refer [admin-page]] [auto-ap.views.pages.home :refer [home-page]] [auto-ap.views.pages.admin.clients :refer [admin-clients-page]] @@ -59,6 +60,9 @@ (defmethod page :sales-orders [_] [sales-orders-page]) +(defmethod page :expected-deposits [_] + [expected-deposits-page]) + (defmethod page :transactions [_] (transactions-page {})) diff --git a/src/cljs/auto_ap/views/pages/pos/expected_deposits.cljs b/src/cljs/auto_ap/views/pages/pos/expected_deposits.cljs new file mode 100644 index 00000000..4a39b7c8 --- /dev/null +++ b/src/cljs/auto_ap/views/pages/pos/expected_deposits.cljs @@ -0,0 +1,72 @@ +(ns auto-ap.views.pages.pos.expected-deposits + (:require [auto-ap.forms :as forms] + [auto-ap.subs :as subs] + [auto-ap.views.components.layouts :refer [side-bar-layout appearing-side-bar]] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.pages.pos.form :as form] + [auto-ap.views.pages.pos.side-bar :as side-bar] + [auto-ap.views.pages.pos.expected-deposits.table :as table] + [auto-ap.views.utils :refer [with-user]] + [clojure.set :as set] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [vimsical.re-frame.fx.track :as track])) + +(re-frame/reg-event-fx + ::params-change + [with-user] + (fn [{:keys [user db ]}[_ params]] + {:graphql {:token user + :owns-state {:single [::data-page/page ::page]} + :query-obj {:venia/queries [[:expected_deposit_page + {:start (:start params 0) + :sort (:sort params) + :per-page (:per-page params) + :total-gte (:amount-gte (:total-range params)) + :total-lte (:amount-lte (:total-range params)) + :date-range (:date-range params) + :client-id (:id @(re-frame/subscribe [::subs/client]))} + [[:expected-deposits [:id :total :fee :location :date + [:client [:name :id]]]] + :total + :start + :end]]]} + :on-success (fn [result] + (let [result (set/rename-keys (:expected-deposit-page result) + {:expected-deposits :data})] + [::data-page/received ::page result]))}})) + + +(re-frame/reg-event-fx + ::unmounted + (fn [{:keys [db]} _] + {:dispatch [::data-page/dispose ::page] + ::track/dispose {:id ::params}})) + +(re-frame/reg-event-fx + ::mounted + (fn [{:keys [db]} _] + {::track/register {:id ::params + :subscription [::data-page/params ::page] + :event-fn (fn [params] + [::params-change params])}})) + +(defn content [] + [:div + [:h1.title "Expected Deposits"] + [table/table {:id :expected-deposits + :data-page ::page}]]) + +(defn expected-deposits-page [] + (reagent/create-class + {:display-name "expected-deposits-page" + :component-will-unmount #(re-frame/dispatch-sync [::unmounted]) + :component-did-mount #(re-frame/dispatch [::mounted]) + :reagent-render + (fn [] + (let [{form-active? :active?} @(re-frame/subscribe [::forms/form ::form/form])] + [side-bar-layout {:side-bar [side-bar/side-bar {:data-page ::page}] + :main [content] + :right-side-bar [appearing-side-bar {:visible? form-active?} + [form/form {}]]}]))})) + diff --git a/src/cljs/auto_ap/views/pages/pos/expected_deposits/table.cljs b/src/cljs/auto_ap/views/pages/pos/expected_deposits/table.cljs new file mode 100644 index 00000000..2234b275 --- /dev/null +++ b/src/cljs/auto_ap/views/pages/pos/expected_deposits/table.cljs @@ -0,0 +1,47 @@ + +(ns auto-ap.views.pages.pos.expected-deposits.table + (:require [auto-ap.subs :as subs] + [auto-ap.views.components.buttons :as buttons] [auto-ap.views.components.grid :as grid] + [auto-ap.views.pages.data-page :as data-page] + [auto-ap.views.pages.pos.form :as form] + [auto-ap.views.utils :refer [date->str nf]] + [clojure.string :as str] + [re-frame.core :as re-frame])) + +(defn row [{sales-order :sales-order + selected-client :selected-client}] + (let [{:keys [client location date total fee id]} sales-order] + [grid/row {:class (:class sales-order) :id id} + (when-not selected-client + [grid/cell {} (:name client)]) + [grid/cell location ] + [grid/cell {} (date->str date) ] + [grid/cell {:class "has-text-right"} (nf total )] + [grid/cell {:class "has-text-right"} (nf fee )] + +[grid/button-cell {} + [:div.buttons + [buttons/fa-icon {:event [::form/editing sales-order] :icon "fa-pencil"}]]]])) + +(defn table [{:keys [data-page]}] + (let [selected-client @(re-frame/subscribe [::subs/client]) + {:keys [data status]} @(re-frame/subscribe [::data-page/page data-page])] + [grid/grid {:data-page data-page + :column-count (if selected-client 7 8)} + [grid/controls data] + [grid/table {:fullwidth true} + [grid/header {} + [grid/row {} + (when-not selected-client + [grid/sortable-header-cell {:sort-key "client" :sort-name "Client"} "Client"]) + [grid/sortable-header-cell {:sort-key "location" :sort-name "Location" :style {:width "7em"}} "Location"] + [grid/sortable-header-cell {:sort-key "date" :sort-name "Date" :style {:width "8em"}} "Date"] + [grid/sortable-header-cell {:sort-key "total" :sort-name "Total" :class "has-text-right" :style {:width "8em"}} "Total"] + [grid/sortable-header-cell {:sort-key "fee" :sort-name "Fee" :class "has-text-right" :style {:width "7em"}} "Fee"] + + [grid/header-cell {:style {:width "4em"}}]]] + [grid/body + (for [sales-order (:data data)] + ^{:key (:id sales-order)} + [row {:sales-order sales-order + :selected-client selected-client}])]]])) diff --git a/src/cljs/auto_ap/views/pages/sales_orders/form.cljs b/src/cljs/auto_ap/views/pages/pos/form.cljs similarity index 99% rename from src/cljs/auto_ap/views/pages/sales_orders/form.cljs rename to src/cljs/auto_ap/views/pages/pos/form.cljs index d6e708f4..58e2d5f0 100644 --- a/src/cljs/auto_ap/views/pages/sales_orders/form.cljs +++ b/src/cljs/auto_ap/views/pages/pos/form.cljs @@ -1,4 +1,4 @@ -(ns auto-ap.views.pages.sales-orders.form +(ns auto-ap.views.pages.pos.form (:require [auto-ap.events :as events] [auto-ap.forms :as forms] diff --git a/src/cljs/auto_ap/views/pages/sales_orders.cljs b/src/cljs/auto_ap/views/pages/pos/sales_orders.cljs similarity index 93% rename from src/cljs/auto_ap/views/pages/sales_orders.cljs rename to src/cljs/auto_ap/views/pages/pos/sales_orders.cljs index 9a277f8c..68965801 100644 --- a/src/cljs/auto_ap/views/pages/sales_orders.cljs +++ b/src/cljs/auto_ap/views/pages/pos/sales_orders.cljs @@ -1,11 +1,11 @@ -(ns auto-ap.views.pages.sales-orders +(ns auto-ap.views.pages.pos.sales-orders (:require [auto-ap.forms :as forms] [auto-ap.subs :as subs] [auto-ap.views.components.layouts :refer [side-bar-layout appearing-side-bar]] [auto-ap.views.pages.data-page :as data-page] - [auto-ap.views.pages.sales-orders.form :as form] - [auto-ap.views.pages.sales-orders.side-bar :as side-bar] - [auto-ap.views.pages.sales-orders.table :as table] + [auto-ap.views.pages.pos.form :as form] + [auto-ap.views.pages.pos.side-bar :as side-bar] + [auto-ap.views.pages.pos.table :as table] [auto-ap.views.utils :refer [with-user]] [clojure.set :as set] [re-frame.core :as re-frame] diff --git a/src/cljs/auto_ap/views/pages/sales_orders/side_bar.cljs b/src/cljs/auto_ap/views/pages/pos/side_bar.cljs similarity index 62% rename from src/cljs/auto_ap/views/pages/sales_orders/side_bar.cljs rename to src/cljs/auto_ap/views/pages/pos/side_bar.cljs index d630ba72..5474ea2b 100644 --- a/src/cljs/auto_ap/views/pages/sales_orders/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/pos/side_bar.cljs @@ -1,4 +1,4 @@ -(ns auto-ap.views.pages.sales-orders.side-bar +(ns auto-ap.views.pages.pos.side-bar (:require [auto-ap.routes :as routes] [auto-ap.subs :as subs] [auto-ap.views.utils :refer [active-when dispatch-value-change]] @@ -13,9 +13,20 @@ (let [ap @(re-frame/subscribe [::subs/active-page]) user @(re-frame/subscribe [::subs/user])] [:div - [:div - + [:div [:p.menu-label "Type"] + [:ul.menu-list + [:li.menu-item + [:a.item {:href (bidi/path-for routes/routes :sales-orders) + :class [(active-when ap = :sales-orders)]} + [:span {:class "icon icon-accounting-invoice-mail" :style {:font-size "25px"}}] + [:span {:class "name"} "Sales Orders"]]] + [:li.menu-item + [:a.item {:href (bidi/path-for routes/routes :expected-deposits) + :class [(active-when ap = :expected-deposits)]} + [:span {:class "icon icon-accounting-invoice-mail" :style {:font-size "25px"}}] + [:span {:class "name"} "Expected Deposits"]]]]] + [:div [:p.menu-label "Date Range"] [:div [date-range-filter diff --git a/src/cljs/auto_ap/views/pages/sales_orders/table.cljs b/src/cljs/auto_ap/views/pages/pos/table.cljs similarity index 91% rename from src/cljs/auto_ap/views/pages/sales_orders/table.cljs rename to src/cljs/auto_ap/views/pages/pos/table.cljs index c7f2a5d2..7bd01c2c 100644 --- a/src/cljs/auto_ap/views/pages/sales_orders/table.cljs +++ b/src/cljs/auto_ap/views/pages/pos/table.cljs @@ -1,9 +1,8 @@ -(ns auto-ap.views.pages.sales-orders.table +(ns auto-ap.views.pages.pos.table (:require [auto-ap.subs :as subs] - [auto-ap.views.components.buttons :as buttons] - [auto-ap.views.components.grid :as grid] + [auto-ap.views.components.buttons :as buttons] [auto-ap.views.components.grid :as grid] [auto-ap.views.pages.data-page :as data-page] - [auto-ap.views.pages.sales-orders.form :as form] + [auto-ap.views.pages.pos.form :as form] [auto-ap.views.utils :refer [date->str nf]] [clojure.string :as str] [re-frame.core :as re-frame]))