diff --git a/src/clj/auto_ap/datomic/migrate.clj b/src/clj/auto_ap/datomic/migrate.clj index 1dba1834..ee85eeab 100644 --- a/src/clj/auto_ap/datomic/migrate.clj +++ b/src/clj/auto_ap/datomic/migrate.clj @@ -256,7 +256,9 @@ {:db/ident :forecasted-transaction/identifier :db/doc "An identifier for this forcasted transaction, e.g., 'RENT'" :db/valueType :db.type/string - :db/cardinality :db.cardinality/one}]]}}] + :db/cardinality :db.cardinality/one}]]} + :auto-ap/add-manager-schema {:txes [[{:db/ident :user-role/manager}]]}} + ] (println "Conforming database...") (c/ensure-conforms conn norms-map) (when (not (seq args)) diff --git a/src/clj/auto_ap/datomic/users.clj b/src/clj/auto_ap/datomic/users.clj index 62b450e2..e9d113b2 100644 --- a/src/clj/auto_ap/datomic/users.clj +++ b/src/clj/auto_ap/datomic/users.clj @@ -18,8 +18,7 @@ :in ['$] :where []} :args [(d/db (d/connect uri))]} - (add-arg '?e id ['?e]) - (doto println))] + (add-arg '?e id ['?e]))] (->> (d/query query) (map first) @@ -47,7 +46,6 @@ (let [new-user-trans @(d/transact (d/connect uri) [(cond-> new-user true (assoc :db/id "user") is-first-user? (assoc :user/role :user-role/admin))])] - (println new-user-trans) (get-by-id (-> new-user-trans :tempids (get "user"))))))) (defn raw-graphql [args] diff --git a/src/clj/auto_ap/graphql.clj b/src/clj/auto_ap/graphql.clj index 2d188213..a89e85af 100644 --- a/src/clj/auto_ap/graphql.clj +++ b/src/clj/auto_ap/graphql.clj @@ -824,9 +824,8 @@ (defn get-user [context args value] (assert-admin (:id context)) - (doto (let [users (d-users/get-graphql args)] - (->graphql users)) - println)) + (let [users (d-users/get-graphql args)] + (->graphql users))) (defn get-vendor [context args value] (->graphql diff --git a/src/clj/auto_ap/graphql/clients.clj b/src/clj/auto_ap/graphql/clients.clj index 2816ee6f..7e4366b2 100644 --- a/src/clj/auto_ap/graphql/clients.clj +++ b/src/clj/auto_ap/graphql/clients.clj @@ -5,10 +5,6 @@ [auto-ap.graphql.utils :refer [->graphql assert-admin can-see-client?]] [clojure.string :as str])) -#_(def role->datomic-role {":none" :user-role/none - ":admin" :user-role/admin - ":user" :user-role/user}) - (defn assert-client-code-is-unique [code] (when (seq (d/query {:query {:find '[?id] :in ['$ '?code] diff --git a/src/clj/auto_ap/graphql/users.clj b/src/clj/auto_ap/graphql/users.clj index 3d859c09..d6e4304d 100644 --- a/src/clj/auto_ap/graphql/users.clj +++ b/src/clj/auto_ap/graphql/users.clj @@ -6,6 +6,7 @@ (def role->datomic-role {":none" :user-role/none ":admin" :user-role/admin + ":manager" :user-role/manager ":user" :user-role/user}) (defn edit-user [context {:keys [edit_user] :as args} value] diff --git a/src/clj/auto_ap/graphql/utils.clj b/src/clj/auto_ap/graphql/utils.clj index e15c30ca..e6b75157 100644 --- a/src/clj/auto_ap/graphql/utils.clj +++ b/src/clj/auto_ap/graphql/utils.clj @@ -72,7 +72,7 @@ (= (:user/role id) "admin") nil - (= (:user/role id) "user") + (#{"manager" "user"} (:user/role id)) (:user/clients id []))) (defn result->page [results result-count key args] diff --git a/src/clj/auto_ap/graphql/vendors.clj b/src/clj/auto_ap/graphql/vendors.clj index b5070a0d..63da3f8f 100644 --- a/src/clj/auto_ap/graphql/vendors.clj +++ b/src/clj/auto_ap/graphql/vendors.clj @@ -11,7 +11,11 @@ (defn upsert-vendor [context {{:keys [id name hidden terms code print_as primary_contact secondary_contact address default_account_id invoice_reminder_schedule terms_overrides account_overrides] :as in} :vendor} value] (when id (assert-admin (:id context))) - (let [terms-overrides (mapv + (let [hidden (if (is-admin? (:id context)) + hidden + false) + + terms-overrides (mapv (fn [to] (cond-> #:vendor-terms-override {:client (:client_id to) diff --git a/src/cljs/auto_ap/subs.cljs b/src/cljs/auto_ap/subs.cljs index b940b7f3..804ecb80 100644 --- a/src/cljs/auto_ap/subs.cljs +++ b/src/cljs/auto_ap/subs.cljs @@ -160,8 +160,11 @@ (re-frame/reg-sub ::vendors - (fn [db] - (filter #(not (:hidden %)) (vals (:vendors db))))) + :<- [::is-admin?] + :<- [::all-vendors] + (fn [[is-admin all-vendors]] + (filter #(or (not (:hidden %)) + is-admin) all-vendors))) (re-frame/reg-sub ::all-vendors diff --git a/src/cljs/auto_ap/views/components/layouts.cljs b/src/cljs/auto_ap/views/components/layouts.cljs index 241644fb..eff197c2 100644 --- a/src/cljs/auto_ap/views/components/layouts.cljs +++ b/src/cljs/auto_ap/views/components/layouts.cljs @@ -55,7 +55,8 @@ (defn navbar [ap] - (let [client (re-frame/subscribe [::subs/client]) + (let [user (re-frame/subscribe [::subs/user]) + client (re-frame/subscribe [::subs/client]) clients (re-frame/subscribe [::subs/clients]) menu (re-frame/subscribe [::subs/menu])] [:nav {:class "navbar has-shadow is-fixed-top"} @@ -74,12 +75,15 @@ [:a.navbar-item {:class [(active-when ap = :payments)] :href (bidi/path-for routes/routes :payments)} "Payments" ] - [:a.navbar-item {:class [(active-when ap = :transactions)] - :href (bidi/path-for routes/routes :transactions)} - "Transactions" ] - [:a.navbar-item {:class [(active-when ap = :ledger)] - :href (bidi/path-for routes/routes :ledger)} - "Ledger" ]] + (when (not= "manager" (:user/role @user)) + [:a.navbar-item {:class [(active-when ap = :transactions)] + :href (bidi/path-for routes/routes :transactions)} + "Transactions" ]) + + (when (not= "manager" (:user/role @user)) + [:a.navbar-item {:class [(active-when ap = :ledger)] + :href (bidi/path-for routes/routes :ledger)} + "Ledger" ])] [:div {:class "navbar-burger burger", :data-target "navMenu"} [:span] [:span] diff --git a/src/cljs/auto_ap/views/components/modal.cljs b/src/cljs/auto_ap/views/components/modal.cljs index 16ea46e0..7c76d8d5 100644 --- a/src/cljs/auto_ap/views/components/modal.cljs +++ b/src/cljs/auto_ap/views/components/modal.cljs @@ -24,7 +24,6 @@ (defn action-modal [{:keys [title class warning action-text id save-event can-submit? status-from] :or {can-submit? true}} & rest] (let [{:keys [visible? saving? error-message]} @(re-frame/subscribe [::subs/modal-state id status-from])] - (println id visible?) (when visible? [:form {:id id :on-submit (fn [e] diff --git a/src/cljs/auto_ap/views/components/vendor_dialog.cljs b/src/cljs/auto_ap/views/components/vendor_dialog.cljs index fcb83000..8ffdb948 100644 --- a/src/cljs/auto_ap/views/components/vendor_dialog.cljs +++ b/src/cljs/auto_ap/views/components/vendor_dialog.cljs @@ -1,6 +1,6 @@ (ns auto-ap.views.components.vendor-dialog (:require [re-frame.core :as re-frame] - [auto-ap.views.utils :refer [dispatch-event horizontal-field bind-field with-user active-when]] + [auto-ap.views.utils :refer [dispatch-event horizontal-field bind-field with-user with-is-admin? active-when]] [auto-ap.views.components.modal :refer [action-modal]] [auto-ap.views.components.address :refer [address-field]] [auto-ap.views.components.typeahead :refer [typeahead-entity]] @@ -105,36 +105,37 @@ (re-frame/reg-event-fx ::save - [with-user (forms/triggers-loading ::vendor-form) (forms/in-form ::vendor-form)] - (fn [{:keys [user] {{:keys [name hidden print-as terms invoice-reminder-schedule primary-contact secondary-contact address default-account terms-overrides account-overrides id] :as data} :data} :db} _] + [with-user with-is-admin? (forms/triggers-loading ::vendor-form) (forms/in-form ::vendor-form)] + (fn [{:keys [user is-admin?] {{:keys [name hidden print-as terms invoice-reminder-schedule primary-contact secondary-contact address default-account terms-overrides account-overrides id] :as data} :data} :db} _] + (println user) (when (s/valid? ::entity/vendor data) { :graphql {:token user :query-obj {:venia/operation {:operation/type :mutation :operation/name "UpsertVendor"} :venia/queries [{:query/data [:upsert-vendor - {:vendor {:id id - :name name - :hidden hidden - :print-as print-as - :terms terms - :terms-overrides (mapv - (fn [{:keys [client override id]}] - {:id id - :client-id (:id client) - :terms override}) - terms-overrides) - :default-account-id (:id default-account) - :account-overrides (mapv - (fn [{:keys [client override id]}] - {:id id - :client-id (:id client) - :account-id (:id override)}) - account-overrides) - :address address - :primary-contact primary-contact - :secondary-contact secondary-contact - :invoice-reminder-schedule invoice-reminder-schedule}} + {:vendor (cond-> {:id id + :name name + :print-as print-as + :terms terms + :terms-overrides (mapv + (fn [{:keys [client override id]}] + {:id id + :client-id (:id client) + :terms override}) + terms-overrides) + :default-account-id (:id default-account) + :account-overrides (mapv + (fn [{:keys [client override id]}] + {:id id + :client-id (:id client) + :account-id (:id override)}) + account-overrides) + :address address + :primary-contact primary-contact + :secondary-contact secondary-contact + :invoice-reminder-schedule invoice-reminder-schedule} + is-admin? (assoc :hidden hidden))} events/vendor-query]}]} :on-success [::save-complete] :on-error [::forms/save-error ::vendor-form]}}))) @@ -171,7 +172,8 @@ (defn form-content [{:keys [data change-event]}] (let [accounts @(re-frame/subscribe [::subs/accounts]) - clients @(re-frame/subscribe [::subs/clients])] + clients @(re-frame/subscribe [::subs/clients]) + is-admin? @(re-frame/subscribe [::subs/is-admin?])] [:div [horizontal-field [:label.label "Name"] @@ -194,15 +196,16 @@ :event change-event :subscription data}]]]] - [horizontal-field - [:label.label "Hidden"] - [:div.control - [bind-field - [:input {:type "checkbox" - :field :hidden - :spec ::entity/hidden - :event change-event - :subscription data}]]]] + (when is-admin? + [horizontal-field + [:label.label "Hidden"] + [:div.control + [bind-field + [:input {:type "checkbox" + :field :hidden + :spec ::entity/hidden + :event change-event + :subscription data}]]]]) [:h2.subtitle "Terms"] [default-with-overrides {:data data :change-event change-event :default-key :terms diff --git a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs index 59fb1d5c..639e6b81 100644 --- a/src/cljs/auto_ap/views/pages/admin/rules/form.cljs +++ b/src/cljs/auto_ap/views/pages/admin/rules/form.cljs @@ -71,6 +71,7 @@ %)) (assoc :vendor-id (:id (:vendor data))) (assoc :yodlee-merchant-id (:id (:yodlee-merchant data))) + (update :description (fn [d] (when d (str/replace d #"\\" "\\\\")))) (update :accounts (fn [as] (map #(-> % (update :id (fn [i] (if (some-> i (str/starts-with? "new-")) @@ -99,6 +100,7 @@ :dom-lte :dom-gte :note]) + (update :description (fn [d] (when d (str/replace d #"\\" "\\\\")))) (assoc :yodlee-merchant-id (:id (:yodlee-merchant data))) (assoc :client-id (:id (:client data))) (assoc :bank-account-id (:id (:bank-account data))))} diff --git a/src/cljs/auto_ap/views/pages/admin/users.cljs b/src/cljs/auto_ap/views/pages/admin/users.cljs index 85361496..9b5a2eeb 100644 --- a/src/cljs/auto_ap/views/pages/admin/users.cljs +++ b/src/cljs/auto_ap/views/pages/admin/users.cljs @@ -149,10 +149,11 @@ :subscription editing} [:option {:value ":none"} "None"] [:option {:value ":user"} "User"] + [:option {:value ":manager"} "Manager"] [:option {:value ":admin"} "Admin"]]]]]] - (when (= ":user" (:role (:user editing))) + (when (#{":user" ":manager"} (:role (:user editing))) [horizontal-field [:label.label "Clients"] [:div.control diff --git a/src/cljs/auto_ap/views/pages/admin/vendors.cljs b/src/cljs/auto_ap/views/pages/admin/vendors.cljs index 1111dd59..0c981a53 100644 --- a/src/cljs/auto_ap/views/pages/admin/vendors.cljs +++ b/src/cljs/auto_ap/views/pages/admin/vendors.cljs @@ -70,7 +70,6 @@ (re-frame/reg-event-db ::save-error (fn [db [_ vendor]] - (println "ERROR?") (-> db (assoc-in [:admin :vendor :error] true) (assoc-in [:admin :vendor :saving?] false)))) @@ -78,7 +77,6 @@ (re-frame/reg-event-db ::change (fn [db [_ path value]] - (println path value) (assoc-in db (concat [:admin :vendor] path) value))) diff --git a/src/cljs/auto_ap/views/pages/ledger.cljs b/src/cljs/auto_ap/views/pages/ledger.cljs index bc5ccca2..f466c249 100644 --- a/src/cljs/auto_ap/views/pages/ledger.cljs +++ b/src/cljs/auto_ap/views/pages/ledger.cljs @@ -91,8 +91,7 @@ (assoc-in [:status :loading] false)))) (defn ledger-content [] - (let [current-client @(re-frame/subscribe [::subs/client]) - user @(re-frame/subscribe [::subs/user])] + (let [current-client @(re-frame/subscribe [::subs/client])] [:div [:h1.title "Ledger"] [table/table {:id :ledger @@ -102,13 +101,16 @@ (defn ledger-page [] - (reagent/create-class - {:display-name "ledger-page" - :component-will-unmount #(re-frame/dispatch [::unmounted]) - :reagent-render - (fn [] - (let [params @(re-frame/subscribe [::params])] - [side-bar-layout - {:side-bar [ledger-side-bar] - :main [ledger-content]}]))})) + (let [user (re-frame/subscribe [::subs/user])] + (reagent/create-class + {:display-name "ledger-page" + :component-will-unmount #(re-frame/dispatch [::unmounted]) + :reagent-render + (fn [] + (if (not= "manager" (:user/role @user)) + (let [params @(re-frame/subscribe [::params])] + [side-bar-layout + {:side-bar [ledger-side-bar] + :main [ledger-content]}]) + [:div "Not authorized"]))}))) diff --git a/src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs b/src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs index 6c8c3d6e..f89b8f44 100644 --- a/src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/balance_sheet.cljs @@ -214,6 +214,9 @@ {:component-will-mount #(re-frame/dispatch-sync [::params-change {:date (date->str (local-now) standard)}]) })) (defn balance-sheet-page [] - [side-bar-layout - {:side-bar [ledger-side-bar] - :main [balance-sheet-content]}]) + (let [user (re-frame/subscribe [::subs/user])] + (if (not= "manager" (:user/role @user)) + [side-bar-layout + {:side-bar [ledger-side-bar] + :main [balance-sheet-content]}] + [:div "Not Authorized"]))) diff --git a/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs b/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs index 1ac61e06..1d69afd1 100644 --- a/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs +++ b/src/cljs/auto_ap/views/pages/ledger/profit_and_loss.cljs @@ -532,10 +532,13 @@ (re-frame/dispatch [::ledger-params-changed params]))}]]])) (defn profit-and-loss-page [] - (let [ledger-list-active? @(re-frame/subscribe [::ledger-list-active?])] - [side-bar-layout - {:side-bar [ledger-side-bar] - :main [profit-and-loss-content] - :right-side-bar [appearing-side-bar - {:visible? ledger-list-active?} - [ledger-list]]}])) + (let [ledger-list-active? @(re-frame/subscribe [::ledger-list-active?]) + user (re-frame/subscribe [::subs/user])] + (if (not= "manager" (:user/role @user)) + [side-bar-layout + {:side-bar [ledger-side-bar] + :main [profit-and-loss-content] + :right-side-bar [appearing-side-bar + {:visible? ledger-list-active?} + [ledger-list]]}] + [:div "Not authorized"]))) diff --git a/src/cljs/auto_ap/views/pages/transactions.cljs b/src/cljs/auto_ap/views/pages/transactions.cljs index 7a9ff745..e5ba2215 100644 --- a/src/cljs/auto_ap/views/pages/transactions.cljs +++ b/src/cljs/auto_ap/views/pages/transactions.cljs @@ -160,14 +160,17 @@ (fn [] (let [{transaction-bar-active? :active?} @(re-frame/subscribe [::forms/form ::edit/form]) params @(re-frame/subscribe [::params]) ;; Keep to make sure it doens'nt get disposed - ap @(re-frame/subscribe [::subs/active-page])] - [side-bar-layout - {:side-bar [side-bar/side-bar] - :main [:div ^{:key approval-status} - [content]] - :bottom [:div - [manual/modal {:import-completed [::manual-import-completed ]}]] - :right-side-bar [appearing-side-bar - {:visible? transaction-bar-active?} - [edit/form {:edit-completed [::edit-completed]}]]}]))})) + ap @(re-frame/subscribe [::subs/active-page]) + user (re-frame/subscribe [::subs/user])] + (if (not= "manager" (:user/role @user)) + [side-bar-layout + {:side-bar [side-bar/side-bar] + :main [:div ^{:key approval-status} + [content]] + :bottom [:div + [manual/modal {:import-completed [::manual-import-completed ]}]] + :right-side-bar [appearing-side-bar + {:visible? transaction-bar-active?} + [edit/form {:edit-completed [::edit-completed]}]]}] + [:div "Not authorized"])))})) diff --git a/src/cljs/auto_ap/views/utils.cljs b/src/cljs/auto_ap/views/utils.cljs index ad4b118a..e180fcb1 100644 --- a/src/cljs/auto_ap/views/utils.cljs +++ b/src/cljs/auto_ap/views/utils.cljs @@ -321,6 +321,15 @@ (-> context (assoc-in [:coeffects :user] (get-in context [:coeffects :db :user])))))) +(def with-is-admin? + (re-frame/->interceptor + :id :with-is-admin? + :before (fn [context] + (-> context + (assoc-in [:coeffects :is-admin?] (= "admin" + (:user/role + (get-in context [:coeffects :db :user])))))))) + (defn query-params [] (reduce-kv (fn [result k v]