diff --git a/scratch-sessions/register_invoices.clj b/scratch-sessions/register_invoices.clj new file mode 100644 index 00000000..ef134103 --- /dev/null +++ b/scratch-sessions/register_invoices.clj @@ -0,0 +1,23 @@ +(ns register-invoices) + +(def totals + + (into [] + (d/q '[:find [(pull ?i [:db/id + :invoice/date + {:invoice/expense-accounts [:db/id + :invoice-expense-account/amount + {:invoice-expense-account/account [:db/id :account/numeric-code]} + :invoice-expense-account/location]}]) ...] + :in $ + :where [?i :invoice/client [:client/code "MBD"]] + [?i :invoice/date ?d] + [(<= ?d #inst "2022-01-01T08:00:00")]] + (d/db conn)))) + +(clojure.data.csv/write-csv *out* + (doall (for [{:keys [:db/id :invoice/expense-accounts :invoice/date]} totals + {:keys [:invoice-expense-account/amount :invoice-expense-account/location :invoice-expense-account/account] :as g} expense-accounts + ] + [id (:db/id g) (:db/id account) (atime/unparse (coerce/to-date-time date) atime/normal-date) amount (:account/numeric-code account) location])) + :separator \tab) diff --git a/src/clj/auto_ap/routes/exports.clj b/src/clj/auto_ap/routes/exports.clj index 9b18ca49..8422ca8d 100644 --- a/src/clj/auto_ap/routes/exports.clj +++ b/src/clj/auto_ap/routes/exports.clj @@ -1,24 +1,27 @@ (ns auto-ap.routes.exports - (:require [auto-ap.datomic :refer [conn]] - [auto-ap.datomic.clients :as d-clients] - [auto-ap.datomic.transactions :as d-transactions] - [clojure.edn :refer [read-string]] - [auto-ap.datomic.vendors :as d-vendors] - [buddy.sign.jwt :as jwt] - [auto-ap.graphql :as graphql] - [auto-ap.graphql.utils :refer [->graphql <-graphql assert-admin assert-can-see-client]] - [auto-ap.routes.utils :refer [wrap-secure]] - [clojure.tools.logging :as log] - [clj-time.coerce :as coerce :refer [to-date]] - [clj-time.core :as time] - [clojure.data.csv :as csv] - [config.core :refer [env]] - [compojure.core :refer [context defroutes GET wrap-routes routes]] - [datomic.api :as d] - [ring.middleware.json :refer [wrap-json-response]] - [venia.core :as venia] - [com.unbounce.dogstatsd.core :as statsd] - [auto-ap.time :as atime])) + (:require + [auto-ap.datomic :refer [conn]] + [auto-ap.datomic.accounts :as accounts] + [auto-ap.datomic.clients :as d-clients] + [auto-ap.datomic.transactions :as d-transactions] + [auto-ap.datomic.vendors :as vendor] + [auto-ap.graphql :as graphql] + [auto-ap.graphql.utils + :refer [->graphql <-graphql assert-admin assert-can-see-client]] + [auto-ap.routes.utils :refer [wrap-secure]] + [auto-ap.time :as atime] + [buddy.sign.jwt :as jwt] + [clj-time.coerce :as coerce :refer [to-date]] + [clj-time.core :as time] + [clojure.data.csv :as csv] + [clojure.edn :refer [read-string]] + [clojure.tools.logging :as log] + [com.unbounce.dogstatsd.core :as statsd] + [compojure.core :refer [context defroutes GET routes wrap-routes]] + [config.core :refer [env]] + [datomic.api :as d] + [ring.middleware.json :refer [wrap-json-response]] + [venia.core :as venia])) (defn wrap-csv-response [handler] (fn [request] @@ -212,6 +215,49 @@ :where [?e :vendor/name]] (d/db conn)) (d/pull-many (d/db conn) d-vendors/default-read))))) + + (GET "/vendors/company/export" {:keys [identity query-params]} + (statsd/time! [(str "export.time") {:tags #{"export:company-vendors"}}] + (let [client (:db/id (d/pull (d/db conn) [:db/id] [:client/code (get query-params "client")])) + + _ (assert-can-see-client identity client) + data (->> (d/q '[:find (pull ?v [:vendor/name + :vendor/terms + {:vendor/default-account [:account/name :account/numeric-code + {:account/client-overrides + [:account-client-override/client + :account-client-override/name]}] + :vendor/terms-overrides [:vendor-terms-override/client + :vendor-terms-override/terms] + :vendor/account-overrides [:vendor-account-override/client + {:vendor-account-override/account [:account/numeric-code :account/name + {:account/client-overrides + [:account-client-override/client + :account-client-override/name]}]}] + :vendor/address [:address/street1 :address/city :address/state :address/zip]}]) + :in $ ?c + :where [?vu :vendor-usage/client ?c] + [?vu :vendor-usage/count ?count] + [(>= ?vu 0)] + [?vu :vendor-usage/vendor ?v]] + (d/db conn) + client) + (map (fn [[v]] + [(-> v :vendor/name) + (-> v :vendor/address :address/street1) + (-> v :vendor/address :address/city) + (-> v :vendor/address :address/state) + (-> v :vendor/address :address/zip) + (-> v (vendor/terms-for-client-id client) ) + (-> v (vendor/account-for-client-id client) (accounts/clientize client) :account/name) + (-> v (vendor/account-for-client-id client) :account/numeric-code) + ] + )) + (into [["Vendor Name" "Address" "City" "State" "Zip" "Terms" "Account" "Account Code"]]))] + {:body + (with-open [w (java.io.StringWriter.)] + (csv/write-csv w data) + (.toString w))}))) (GET "/ledger/export" {:keys [identity query-params]} (let [start-date (or (some-> (query-params "start-date") (atime/parse atime/iso-date)) diff --git a/src/cljc/auto_ap/client_routes.cljc b/src/cljc/auto_ap/client_routes.cljc index 5de1c90d..b2665b01 100644 --- a/src/cljc/auto_ap/client_routes.cljc +++ b/src/cljc/auto_ap/client_routes.cljc @@ -31,6 +31,8 @@ "requires-feedback" :requires-feedback-transactions "excluded" :excluded-transactions} "reports/" {"" :reports} + "company/" {"other" :company-other + "export-vendors" :company-export-vendors} "plaid" :plaid "yodlee2" :yodlee2 "ledger/" {"" :ledger diff --git a/src/cljs/auto_ap/views/main.cljs b/src/cljs/auto_ap/views/main.cljs index 3c539c64..0560a1f0 100644 --- a/src/cljs/auto_ap/views/main.cljs +++ b/src/cljs/auto_ap/views/main.cljs @@ -30,7 +30,8 @@ [auto-ap.views.pages.admin.users :refer [admin-users-page]] [auto-ap.views.pages.admin.import-batches :refer [import-batches-page]] [auto-ap.views.pages.company.yodlee2 :as yodlee2] - [auto-ap.views.pages.company.plaid :as plaid])) + [auto-ap.views.pages.company.plaid :as plaid] + [auto-ap.views.pages.company.other :as company-other])) (defmulti page (fn [active-page] active-page)) (defmethod page :unpaid-invoices [_] @@ -121,6 +122,9 @@ (defmethod page :yodlee2 [_] (yodlee2/admin-yodlee-provider-accounts-page)) +(defmethod page :company-other [_] + (company-other/company-other-page)) + (defmethod page :plaid [_] (plaid/plaid-page)) diff --git a/src/cljs/auto_ap/views/pages/company/other.cljs b/src/cljs/auto_ap/views/pages/company/other.cljs new file mode 100644 index 00000000..c676a4d3 --- /dev/null +++ b/src/cljs/auto_ap/views/pages/company/other.cljs @@ -0,0 +1,76 @@ +(ns auto-ap.views.pages.company.other + (:require + [auto-ap.subs :as subs] + [auto-ap.views.utils :refer [dispatch-event with-user]] + [auto-ap.views.components.buttons :refer [fa-icon]] + [auto-ap.views.components.layouts :refer [side-bar-layout]] + [auto-ap.views.pages.company.side-bar :refer [company-side-bar]] + [re-frame.core :as re-frame] + [reagent.core :as reagent] + [vimsical.re-frame.cofx.inject :as inject] + [auto-ap.status :as status])) + +(re-frame/reg-event-fx + ::ready + (fn [{:keys [db] } [_ result]] + {:db (assoc db ::export-result result)})) + +(re-frame/reg-sub + ::export-result + (fn [db] + (::export-result db))) + +(re-frame/reg-event-fx + ::export-vendors + [(re-frame/inject-cofx ::inject/sub [::subs/client]) with-user] + (fn [{:keys [db user] ::subs/keys [client]}] + {:http {:token user + :method :get + :headers {"Content-Type" "application/edn"} + :uri (str "/api/vendors/company/export?client=" (:code client)) + :owns-state {:single ::export-vendors} + :on-success [::ready]}})) + +(re-frame/reg-event-fx + ::unmounted + (fn [{:keys [db]} _] + {:db (dissoc db ::export-result)})) + +(defn company-other-content [] + (let [client @(re-frame/subscribe [::subs/client]) + state @(re-frame/subscribe [::status/single ::export-vendors]) + export-result @(re-frame/subscribe [::export-result])] + (if client + [:div + [:h1.title "Other"] + [status/status-notification [::status/single ::export-vendors]] + + + [fa-icon {:icon "fa-external-link" + :class (into (status/class-for state) ["is-primary" ]) + :disabled (status/disabled-for state) + :event [::export-vendors] + } + [:div + (str " Export " (:name client) " Vendors") + ]] + (when export-result + [:<> + [:div + "Ready! Click " + [:a {:href (str "data:attachment/csv;charset=utf-8," (js/encodeURI export-result)) + :target "_blank" + :download (str "vendors-" (:code client) ".csv")} + "here"] + " to download"]]) + ] + [:div "Please select a company."]))) + + +(defn company-other-page [] + (reagent/create-class + {:reagent-render (fn [] + [side-bar-layout {:side-bar [company-side-bar {}] + :main [company-other-content]}]) + :component-will-unmount #(re-frame/dispatch [::unmounted])})) + diff --git a/src/cljs/auto_ap/views/pages/company/side_bar.cljs b/src/cljs/auto_ap/views/pages/company/side_bar.cljs index a6d4ff50..d135bfe1 100644 --- a/src/cljs/auto_ap/views/pages/company/side_bar.cljs +++ b/src/cljs/auto_ap/views/pages/company/side_bar.cljs @@ -22,4 +22,8 @@ [:li.menu-item [:a {:href (bidi/path-for routes/routes :yodlee2), :class (str "item" (active-when ap = :yodlee2))} [:span {:class "icon icon-saving-bank-1" :style {:font-size "25px"}}] - [:span {:class "name"} "Yodlee Link"]]]])) + [:span {:class "name"} "Yodlee Link"]]] + [:li.menu-item + [:a {:href (bidi/path-for routes/routes :company-other), :class (str "item" (active-when ap = :company-other))} + [:span {:class "icon icon-cog-play-1" :style {:font-size "25px"}}] + [:span {:class "name"} "Other"]]]]))