@@ -37,7 +37,6 @@
|
||||
"yodlee2" :yodlee2
|
||||
"ledger/" {"" :ledger
|
||||
"profit-and-loss" :profit-and-loss
|
||||
"cash-flows" :cash-flows
|
||||
"profit-and-loss-detail" :profit-and-loss-detail
|
||||
"balance-sheet" :balance-sheet
|
||||
"external" :external-ledger
|
||||
|
||||
@@ -30,11 +30,7 @@
|
||||
:payroll [60000 69999]
|
||||
:controllable [70000 79999]
|
||||
:fixed-overhead [80000 89999]
|
||||
:ownership-controllable [90000 99999]
|
||||
:operating-activities [10000 99999]
|
||||
:investment-activities [10000 99999]
|
||||
:financing-activities [10000 99999]
|
||||
:bottom-line [10000 99999]})
|
||||
:ownership-controllable [90000 99999]})
|
||||
|
||||
(def groupings
|
||||
{:assets [["1100 Cash and Bank Accounts" 11000 11999]
|
||||
@@ -85,16 +81,7 @@
|
||||
["94000-96000 Other Owner Controllable Costs" 94000 95999]
|
||||
["96000 Depreciation" 96000 96999]
|
||||
["97000 Taxes" 97000 97999]
|
||||
["98000 Other Expenses" 98000 98999]]
|
||||
|
||||
:operating-activities [["12000-14000 Accounts Receivable" 12000 13999]
|
||||
["21000-28000 Accounts Payable" 21000 27999]
|
||||
["96000-97000 Amortization" 96000 96999]]
|
||||
|
||||
:investment-activities [["14000-18000 Investments" 14000 17999]]
|
||||
:financing-activities [["28000-35000 Financing Activities" 28000 34999]
|
||||
["35001-40000 Other" 35001 39999]]
|
||||
:bottom-line [["11000-12000 Bottom Line" 11000 11999]]})
|
||||
["98000 Other Expenses" 98000 98999]]})
|
||||
|
||||
(defn in-range? [code]
|
||||
(if code
|
||||
@@ -459,67 +446,6 @@
|
||||
{:header (headers pnl-datas title)
|
||||
:rows (combine-tables pnl-datas table percent-of-sales deltas)}))
|
||||
|
||||
(defn cash-flows-location-detail-table [pnl-datas #_client-datas title prefix]
|
||||
(let [table (-> []
|
||||
(into (detail-rows pnl-datas
|
||||
:operating-activities
|
||||
(str prefix " Operating Activities")))
|
||||
(into (detail-rows pnl-datas
|
||||
:investment-activities
|
||||
(str prefix " Investment Activities")))
|
||||
(into (detail-rows pnl-datas
|
||||
:financing-activities
|
||||
(str prefix " Financing Activities")))
|
||||
|
||||
(into (detail-rows pnl-datas
|
||||
:bottom-line
|
||||
(str prefix " Bottom Line")))
|
||||
#_(into (detail-rows pnl-datas
|
||||
:cogs
|
||||
(str prefix " COGS")))
|
||||
#_(into (detail-rows
|
||||
pnl-datas
|
||||
:payroll
|
||||
(str prefix " Payroll")))
|
||||
#_(conj (subtotal-by-column-row (map #(filter-categories % [:payroll :cogs])
|
||||
pnl-datas)
|
||||
(str prefix " Prime Costs")))
|
||||
#_(conj (subtotal-by-column-row (map #(-> %
|
||||
(filter-categories [:sales :payroll :cogs])
|
||||
(negate #{:payroll :cogs}))
|
||||
pnl-datas)
|
||||
(str prefix " Gross Profits")))
|
||||
#_(into (detail-rows
|
||||
pnl-datas
|
||||
:controllable
|
||||
(str prefix " Controllable Expenses")))
|
||||
#_(into (detail-rows
|
||||
pnl-datas
|
||||
:fixed-overhead
|
||||
(str prefix " Fixed Overhead")))
|
||||
#_(into (detail-rows
|
||||
pnl-datas
|
||||
:ownership-controllable
|
||||
(str prefix " Ownership Controllable")))
|
||||
#_(conj (subtotal-by-column-row (map #(filter-categories % [:controllable :fixed-overhead :ownership-controllable]) pnl-datas)
|
||||
(str prefix " Overhead")))
|
||||
#_(conj (subtotal-by-column-row (map #(-> %
|
||||
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
|
||||
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
|
||||
pnl-datas)
|
||||
(str prefix " Net Income"))))
|
||||
#_table #_(if (seq client-datas)
|
||||
(conj table (subtotal-by-column-row (map #(-> %
|
||||
(filter-categories [:sales :cogs :payroll :controllable :fixed-overhead :ownership-controllable])
|
||||
(negate #{:cogs :payroll :controllable :fixed-overhead :ownership-controllable}))
|
||||
client-datas)
|
||||
"All Location Net Income"))
|
||||
table)
|
||||
percent-of-sales (calc-percent-of-sales table pnl-datas)
|
||||
deltas (into [] (calc-deltas table))]
|
||||
{:header (headers pnl-datas title)
|
||||
:rows (combine-tables pnl-datas table percent-of-sales deltas)}))
|
||||
|
||||
(defn warning-message [pnl-data]
|
||||
(let [errors (->> pnl-data
|
||||
:data
|
||||
@@ -587,36 +513,6 @@
|
||||
(str (-> pnl-data :clients-by-id (get client-id)) " (" location ") Detail")
|
||||
location))))})
|
||||
|
||||
(defn summarize-cash-flows [pnl-data]
|
||||
(let [client-ids (->> (client-locations pnl-data)
|
||||
(map first)
|
||||
set)]
|
||||
{:warning (warning-message pnl-data)
|
||||
:details
|
||||
(doall (if (-> pnl-data :args :column-per-location)
|
||||
[(cash-flows-location-detail-table (mapcat identity (for [[period i] (map vector (-> pnl-data :args :periods ) (range))]
|
||||
(concat
|
||||
(for [[client-id location] (client-locations pnl-data)]
|
||||
(-> pnl-data
|
||||
(filter-client client-id)
|
||||
(filter-location location)
|
||||
(filter-period period)
|
||||
(zebra i)))
|
||||
[(-> pnl-data
|
||||
(filter-period period)
|
||||
(zebra i))])))
|
||||
"All location Detail"
|
||||
"")]
|
||||
(for [client-id client-ids]
|
||||
(cash-flows-location-detail-table (for [[period i] (map vector (-> pnl-data :args :periods ) (range))]
|
||||
(-> pnl-data
|
||||
(filter-client client-id)
|
||||
(filter-location "A")
|
||||
(filter-period period)
|
||||
(zebra i)))
|
||||
(str (-> pnl-data :clients-by-id (get client-id)) " Detail")
|
||||
""))))}))
|
||||
|
||||
|
||||
(defn balance-sheet-headers [pnl-data]
|
||||
[(cond-> [{:value "Period Ending"}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
[auto-ap.views.pages.ledger.external-import :refer [external-import-page]]
|
||||
[auto-ap.views.pages.ledger.external-ledger :refer [external-ledger-page]]
|
||||
[auto-ap.views.pages.ledger.profit-and-loss :refer [profit-and-loss-page]]
|
||||
[auto-ap.views.pages.ledger.cash-flows :refer [cash-flows-page]]
|
||||
[auto-ap.views.pages.ledger.profit-and-loss-detail :refer [profit-and-loss-detail-page]]
|
||||
[auto-ap.views.pages.login :refer [login-page]]
|
||||
[auto-ap.views.pages.payments :refer [payments-page]]
|
||||
@@ -88,8 +87,6 @@
|
||||
(defmethod page :profit-and-loss [_]
|
||||
(profit-and-loss-page))
|
||||
|
||||
(defmethod page :cash-flows [_]
|
||||
(cash-flows-page))
|
||||
|
||||
(defmethod page :profit-and-loss-detail [_]
|
||||
(profit-and-loss-detail-page))
|
||||
|
||||
@@ -1,508 +0,0 @@
|
||||
(ns auto-ap.views.pages.ledger.cash-flows
|
||||
(:require
|
||||
[auto-ap.forms :as forms]
|
||||
[auto-ap.ledger.reports :as l-reports]
|
||||
[auto-ap.status :as status]
|
||||
[auto-ap.subs :as subs]
|
||||
[auto-ap.views.pages.ledger.report-table :as rtable]
|
||||
[auto-ap.views.components.buttons :as buttons]
|
||||
[auto-ap.views.components.layouts
|
||||
:refer [appearing-side-bar side-bar-layout]]
|
||||
[auto-ap.views.components.modal :as modal]
|
||||
[auto-ap.views.components.switch-field :refer [switch-field]]
|
||||
[auto-ap.views.pages.data-page :as data-page]
|
||||
[auto-ap.views.pages.ledger.side-bar :refer [ledger-side-bar]]
|
||||
[auto-ap.views.pages.ledger.table :as ledger-table]
|
||||
[auto-ap.views.utils
|
||||
:refer [date->str
|
||||
date-picker
|
||||
dispatch-event
|
||||
local-today
|
||||
query-params
|
||||
standard
|
||||
str->date
|
||||
with-user]]
|
||||
[cljs-time.core :as t]
|
||||
[clojure.set :as set]
|
||||
[clojure.string :as str]
|
||||
[re-frame.core :as re-frame]
|
||||
[react-dom :as react-dom]
|
||||
[reagent.core :as reagent]
|
||||
[vimsical.re-frame.cofx.inject :as inject]
|
||||
[vimsical.re-frame.fx.track :as track]
|
||||
[auto-ap.forms.builder :as form-builder]
|
||||
[auto-ap.views.components :as com]))
|
||||
|
||||
|
||||
|
||||
(defn and-last-year [{:keys [start end] :as p1}]
|
||||
[p1
|
||||
{:start (t/minus start (t/years 1))
|
||||
:end (t/minus end (t/years 1))}])
|
||||
|
||||
(defn encode-period [p]
|
||||
{:start (date->str (:start p) standard)
|
||||
:end (date->str (:end p) standard)})
|
||||
|
||||
|
||||
;; SUBS
|
||||
|
||||
(re-frame/reg-sub
|
||||
::ledger-list-active?
|
||||
(fn [db]
|
||||
(-> db ::ledger-list-active?)))
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::received
|
||||
[(forms/in-form ::form)]
|
||||
(fn [db [_ data]]
|
||||
(-> db (assoc :report (:profit-and-loss data)))))
|
||||
|
||||
;; EVENTS
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::ledger-list-closing
|
||||
(fn [db]
|
||||
(assoc db ::ledger-list-active? false)))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::report-requested
|
||||
[with-user (forms/in-form ::form)]
|
||||
(fn [{:keys [db user]}]
|
||||
(cond-> {:graphql {:token user
|
||||
:owns-state {:single ::page}
|
||||
:query-obj {:venia/queries [[:profit-and-loss
|
||||
{:client-ids (map (comp :id :client) (:clients (:data db)))
|
||||
:periods (mapv #(select-keys % #{:start :end} ) (:periods (:data db)))
|
||||
:include-deltas (:include-deltas (:data db))
|
||||
:column-per-location (:column-per-location (:data db))}
|
||||
[[:periods [[:accounts [:name :amount :client_id :account-type :id :count :numeric-code :location]]]]]]]}
|
||||
:on-success [::received]}
|
||||
:set-uri-params {:periods (mapv
|
||||
encode-period
|
||||
(:periods (:data db)))
|
||||
:clients (mapv #(select-keys (:client %) [:name :id]) (:clients (:data db))) }
|
||||
:db (-> db
|
||||
(dissoc :report)
|
||||
(update-in [:data :clients] #(into [] (filter seq %))))})))
|
||||
|
||||
(defn email-body [report-url]
|
||||
(js/encodeURIComponent
|
||||
(str
|
||||
"Hello,
|
||||
Click here (" report-url ") to download your financial reports. We have not finished reviewing and reconciling these numbers with you. Please review and let us know if anything seems missing or in need of correction.
|
||||
Click here (http://app.integreatconsult.com/) to login to the Financials app to review the details here.
|
||||
Click here (https://share.vidyard.com/watch/MHTo5PyXPxXUpVH93RWFM9?) for a video on how to run a P&L on your own.
|
||||
To see a history of past financial reports, click here: https://app.integreatconsult.com/reports/
|
||||
|
||||
NOTE: Please review the transactions we may have question for you here: https://app.integreatconsult.com/transactions/requires-feedback. You can either edit the transaction to what expense account it should be or email back what it should be.")))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::received-pdf
|
||||
[(re-frame/inject-cofx ::inject/sub [::subs/clients-by-id])]
|
||||
(fn [{:keys [db ::subs/clients-by-id]} [_ result]]
|
||||
(let [selected-clients (-> db ::forms/forms ::form :data :clients)
|
||||
single-client? (= (count selected-clients)
|
||||
1)
|
||||
client-emails (when single-client?
|
||||
(-> clients-by-id
|
||||
(get (:id (:client (first selected-clients))))
|
||||
:emails))]
|
||||
{:dispatch [::modal/modal-requested {:title "Your report is ready"
|
||||
:body [:div
|
||||
[:div "Click "
|
||||
[:a {:href (-> result :profit-and-loss-pdf :url) :target "_new"} "here"] " to view it."]
|
||||
(when (and single-client? (seq client-emails))
|
||||
[:div "Once you've confirmed you're happy with it, click "
|
||||
[:a {:href (str "mailto:" (str/join ";" (map :email client-emails)) "?body=" (email-body (-> result :profit-and-loss-pdf :url))
|
||||
"&subject=" (-> result :profit-and-loss-pdf :name) " is ready")}
|
||||
"here"] " to open your email client and to send it to " (str/join "," (map (fn [e]
|
||||
(str (:email e) " (" (:description e) ")"))
|
||||
client-emails)) "."])]}]})))
|
||||
(re-frame/reg-event-fx
|
||||
::export-pdf
|
||||
[with-user (forms/in-form ::form)]
|
||||
(fn [{:keys [db user]}]
|
||||
(cond-> {:graphql {:token user
|
||||
:owns-state {:single ::page}
|
||||
:query-obj {:venia/queries [[:profit-and-loss-pdf
|
||||
{:client-ids (map (comp :id :client) (:clients (:data db)))
|
||||
:include-deltas (:include-deltas (:data db))
|
||||
:column-per-location (:column-per-location (:data db))
|
||||
:periods (mapv #(select-keys % #{:start :end}) (:periods (:data db)))}
|
||||
[:url :name]]]}
|
||||
:on-success [::received-pdf]}
|
||||
:set-uri-params {:periods (mapv encode-period (:periods (:data db)))
|
||||
:clients (mapv #(select-keys (:client %) [:name :id]) (:clients (:data db))) }
|
||||
:db (dissoc db :report)})))
|
||||
|
||||
|
||||
(re-frame/reg-event-db
|
||||
::change-internal
|
||||
(forms/change-handler ::form
|
||||
(fn [_ field value]
|
||||
(cond
|
||||
(= [:periods] field)
|
||||
[field value]
|
||||
|
||||
(and (= :periods (first field))
|
||||
(= 2 (count field)))
|
||||
[field value] ;;already serialized
|
||||
|
||||
(= :periods (first field)) [field value ]
|
||||
|
||||
:else nil)
|
||||
)))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::change
|
||||
[with-user (forms/in-form ::form)]
|
||||
(fn [{:keys [db]} [_ & event]]
|
||||
{:db (dissoc db :report)
|
||||
:dispatch-n [(into [::change-internal] event)
|
||||
[::ledger-list-closing]]}))
|
||||
|
||||
|
||||
(defn data-params->query-params [params]
|
||||
(when params
|
||||
{:start (:start params 0)
|
||||
:sort (:sort params)
|
||||
:per-page (:per-page params)
|
||||
:vendor-id (:id (:vendor params))
|
||||
:client-id (:client-id params)
|
||||
:from-numeric-code (:from-numeric-code params)
|
||||
:to-numeric-code (:to-numeric-code params)
|
||||
:location (:location params)
|
||||
:date-range (:date-range params)}))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::ledger-params-change
|
||||
[with-user]
|
||||
(fn [{:keys [user]} [_ ledger-params]]
|
||||
(when (seq ledger-params)
|
||||
{:graphql {:token user
|
||||
:owns-state {:single [::data-page/page ::ledger]}
|
||||
:query-obj {:venia/queries [[:ledger-page
|
||||
{:filters (data-params->query-params ledger-params)}
|
||||
[[:journal-entries [:id
|
||||
:source
|
||||
:original-entity
|
||||
:note
|
||||
:amount
|
||||
:alternate-description
|
||||
[:vendor
|
||||
[:name :id]]
|
||||
[:client
|
||||
[:name :id]]
|
||||
[:line-items
|
||||
[:id :debit :credit :location :running-balance
|
||||
[:account [:id :name]]]]
|
||||
:date]]
|
||||
:total
|
||||
:start
|
||||
:end]]]}
|
||||
:on-success (fn [result]
|
||||
[::data-page/received ::ledger (set/rename-keys (:ledger-page result)
|
||||
{:journal-entries :data})])}})))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::investigate-clicked
|
||||
(fn [{:keys [db]} [_ {:keys [location from-numeric-code to-numeric-code client-id]
|
||||
{:keys [start end]} :date-range}]]
|
||||
{:db (-> db (assoc ::ledger-list-active? true))
|
||||
:dispatch [::data-page/additional-params-changed ::ledger {:client-id client-id
|
||||
:from-numeric-code from-numeric-code
|
||||
:to-numeric-code to-numeric-code
|
||||
:location location
|
||||
:date-range {:start (date->str start standard)
|
||||
:end (date->str end standard)}}]}))
|
||||
|
||||
|
||||
(re-frame/reg-sub
|
||||
::can-submit
|
||||
(fn [_]
|
||||
true))
|
||||
|
||||
|
||||
(defn report-control-detail [{:keys [active box which]} children]
|
||||
(when (and @box
|
||||
(= which @active))
|
||||
(react-dom/createPortal (reagent/as-element
|
||||
[:div.notification.is-light
|
||||
[:a.delete {:on-click (fn [] (reset! active nil))}]
|
||||
children
|
||||
])
|
||||
@box)))
|
||||
|
||||
(defn period-preset-button [{:keys [title periods]}]
|
||||
(let [{{:keys [selected-preset]} :data} @(re-frame/subscribe [::forms/form ::form])]
|
||||
[:div.control
|
||||
[:a.button
|
||||
{:class (when (= selected-preset title) "is-active")
|
||||
:on-click (fn []
|
||||
(re-frame/dispatch-sync [::change
|
||||
[:periods]
|
||||
periods
|
||||
[:selected-preset] title])
|
||||
(re-frame/dispatch-sync [::change
|
||||
[:show-advanced?]
|
||||
false]))}
|
||||
title]]))
|
||||
|
||||
(defn report-controls []
|
||||
(let [!box (atom nil)
|
||||
active (reagent/atom nil)]
|
||||
(fn []
|
||||
(let [{:keys [data]} @(re-frame/subscribe [::forms/form ::form])
|
||||
{:keys [selected-preset include-deltas column-per-location]} data]
|
||||
[form-builder/builder {:can-submit [::can-submit]
|
||||
:change-event [::change]
|
||||
:submit-event [::report-requested]
|
||||
:id ::form}
|
||||
[:div.report-controls
|
||||
[:div.level.mb-2
|
||||
[:div.level-left
|
||||
[:div.level-item
|
||||
[buttons/dropdown {:on-click (fn [] (reset! active :clients))}
|
||||
[:span (str "Companies"
|
||||
(when-let [clients (:clients data)]
|
||||
(str " (" (str/join ", " (map (comp :name :client) clients)) ")")))]]
|
||||
[report-control-detail {:active active :box !box :which :clients}
|
||||
[:div {:style {:width "20em"}}
|
||||
[:h4.subtitle "Companies"]
|
||||
[form-builder/raw-field-v2 {:field :clients}
|
||||
[com/multi-field-v2 {:new-text "Add another company"
|
||||
:template [[form-builder/raw-field-v2 {:field :client}
|
||||
[com/entity-typeahead {:entities @(re-frame/subscribe [::subs/clients])
|
||||
:style {:width "18em"}
|
||||
:entity->text :name}]]]
|
||||
:key-fn :id}]]]]]
|
||||
[:div.level-item
|
||||
[buttons/dropdown {:on-click (fn [] (reset! active :range))}
|
||||
[:span (str "Range"
|
||||
(when selected-preset
|
||||
(str " (" selected-preset ")")))]]
|
||||
[report-control-detail {:active active :box !box :which :range}
|
||||
[:div
|
||||
[:h4.subtitle "Range"]
|
||||
[:div.field.is-grouped
|
||||
[:div.control
|
||||
[:div.field.has-addons
|
||||
[:div.control
|
||||
[form-builder/raw-field-v2 {:field :thirteen-periods-end}
|
||||
[date-picker {:placeholder "End date"
|
||||
:output :cljs-date}]]]
|
||||
[period-preset-button {:title "13 periods"
|
||||
:periods (let [today (or (some-> (:thirteen-periods-end data))
|
||||
(local-today))]
|
||||
(into
|
||||
[{:start (t/plus (t/minus today (t/weeks (* 13 4)))
|
||||
(t/days 1))
|
||||
:end today
|
||||
:title "Total"}]
|
||||
(for [i (range 13)]
|
||||
{:start (t/plus (t/minus today (t/weeks (* (inc i) 4)))
|
||||
(t/days 1))
|
||||
:end (t/minus today (t/weeks (* i 4)))})))}]]]
|
||||
|
||||
[:div.control
|
||||
[:div.field.has-addons
|
||||
[:div.control
|
||||
[form-builder/raw-field-v2 {:field :twelve-periods-end}
|
||||
[date-picker {:placeholder "End date"
|
||||
:output :cljs-date}]]]
|
||||
[period-preset-button {:title "12 months"
|
||||
:periods (let [end-date (or (some-> (:twelve-periods-end data))
|
||||
(local-today))
|
||||
this-month (t/local-date (t/year end-date)
|
||||
(t/month end-date)
|
||||
1)]
|
||||
(into
|
||||
[{:start (t/minus this-month (t/months 11))
|
||||
:end (t/minus (t/plus this-month (t/months 1))
|
||||
(t/days 1))
|
||||
:title "Total"}]
|
||||
(for [i (range 12)]
|
||||
{:start (t/minus this-month (t/months (- 11 i)))
|
||||
:end (t/minus (t/minus this-month (t/months (- 10 i)))
|
||||
(t/days 1))})))}]]]
|
||||
|
||||
[period-preset-button {:periods (let [last-sunday (loop [current (local-today)]
|
||||
(if (= 7 (t/day-of-week current))
|
||||
current
|
||||
(recur (t/minus current (t/period :days 1)))))]
|
||||
(and-last-year {:start (t/minus last-sunday (t/period :days 6))
|
||||
:end last-sunday}))
|
||||
:title "Last week"}]
|
||||
|
||||
[period-preset-button {:periods (and-last-year {:start (loop [current (local-today)]
|
||||
(if (= 1 (t/day-of-week current))
|
||||
current
|
||||
(recur (t/minus current (t/period :days 1)))))
|
||||
:end (local-today)})
|
||||
:title "Week to date"}]
|
||||
|
||||
[period-preset-button {:periods (and-last-year {:start (t/minus (t/local-date (t/year (local-today))
|
||||
(t/month (local-today))
|
||||
1)
|
||||
(t/period :months 1))
|
||||
:end (t/minus (t/local-date (t/year (local-today))
|
||||
(t/month (local-today))
|
||||
1)
|
||||
(t/period :days 1))})
|
||||
:title "Last month"}]
|
||||
|
||||
[period-preset-button {:periods (and-last-year {:start (t/local-date (t/year (local-today))
|
||||
(t/month (local-today))
|
||||
1)
|
||||
:end (local-today)})
|
||||
:title "Month to date"}]
|
||||
|
||||
[period-preset-button {:periods (and-last-year {:start (t/local-date (t/year (local-today)) 1 1)
|
||||
:end
|
||||
(local-today)})
|
||||
:title "Year to date"}]
|
||||
|
||||
[period-preset-button {:periods [{:start (t/local-date (dec (t/year (local-today))) 1 1)
|
||||
:end (t/local-date (dec (t/year (local-today))) 12 31)}]
|
||||
:title "Last calendar year"}]
|
||||
|
||||
[period-preset-button {:periods (and-last-year {:start (t/plus (t/minus (local-today) (t/period :years 1))
|
||||
(t/period :days 1))
|
||||
:end (local-today)})
|
||||
:title "Full year"}]]
|
||||
[:div
|
||||
[form-builder/raw-field-v2 {:field :show-advanced?}
|
||||
[com/checkbox {:label "Show Advanced"}]]]
|
||||
(when (:show-advanced? data)
|
||||
[form-builder/raw-field-v2 {:field :periods}
|
||||
[com/multi-field-v2 {:template [[form-builder/raw-field-v2 {:field :start}
|
||||
[date-picker {:output :cljs-date}]]
|
||||
[form-builder/raw-field-v2 {:field :end}
|
||||
[date-picker {:output :cljs-date}]]]}]])]]]
|
||||
|
||||
[:div.level-item
|
||||
[:div
|
||||
[switch-field {:id "include-deltas"
|
||||
:checked (boolean include-deltas)
|
||||
:on-change (fn [e]
|
||||
(re-frame/dispatch [::change
|
||||
[:include-deltas] (.-checked (.-target e))]))
|
||||
:label "Include deltas"
|
||||
:type "checkbox"}]]]
|
||||
[:div.level-item
|
||||
[:div
|
||||
[switch-field {:id "column-per-location"
|
||||
:checked (boolean column-per-location)
|
||||
:on-change (fn [e]
|
||||
(re-frame/dispatch [::change
|
||||
[:column-per-location] (.-checked (.-target e))]))
|
||||
:label "Column per location"
|
||||
:type "checkbox"}]]]]
|
||||
[:div.level-right
|
||||
[:div.buttons
|
||||
|
||||
(when @(re-frame/subscribe [::subs/is-admin?])
|
||||
[:button.button.is-secondary {:on-click (dispatch-event [::export-pdf])} "Export"])
|
||||
[:button.button.is-primary "Run"]]
|
||||
|
||||
]]
|
||||
[:div.report-control-detail {:ref (fn [el]
|
||||
(when (not= @!box el)
|
||||
(reset! !box el)))}]]]))))
|
||||
|
||||
|
||||
|
||||
(defn pnl-report [{:keys [args report-data]}]
|
||||
(let [pnl-data (->> report-data
|
||||
:periods
|
||||
(mapcat (fn [p1 p2]
|
||||
(map
|
||||
(fn [a]
|
||||
(assoc a :period p1
|
||||
:amount (js/parseFloat (:amount a)))
|
||||
)
|
||||
(:accounts p2)))
|
||||
(:periods args)))
|
||||
client-codes (->> @(re-frame/subscribe [::subs/clients-by-id])
|
||||
(map (fn [[k v]]
|
||||
[k (:code v)]))
|
||||
(into {}))
|
||||
pnl-data (l-reports/->PNLData args pnl-data client-codes)
|
||||
report (l-reports/summarize-cash-flows pnl-data)
|
||||
table (rtable/concat-tables (:details report))]
|
||||
[:div
|
||||
[:h1.title "Profit and Loss - " (str/join ", " (map (comp :name :client) (:clients args)))]
|
||||
(when (:warning report)
|
||||
[:div.notification.is-warning.is-light
|
||||
(:warning report)])
|
||||
[rtable/table {:widths (into [20] (take (dec (rtable/cell-count table))
|
||||
(mapcat identity
|
||||
(repeat
|
||||
(if (-> pnl-data :args :include-deltas)
|
||||
[13 6 13]
|
||||
[13 6])))))
|
||||
:click-event ::investigate-clicked
|
||||
:table table}]]))
|
||||
|
||||
|
||||
(defn profit-and-loss-content []
|
||||
(let [status @(re-frame/subscribe [::status/single ::page])
|
||||
{:keys [data report]} @(re-frame/subscribe [::forms/form ::form])]
|
||||
[:div
|
||||
[:div
|
||||
[status/status-notification {:statuses [[::status/single ::page]]}]
|
||||
[report-controls]]
|
||||
[status/big-loader status]
|
||||
(when (and (not= :loading (:state status))
|
||||
report)
|
||||
[pnl-report {:report-data report
|
||||
:args data}])]))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::unmounted-pnl
|
||||
(fn [{:keys [_]} _]
|
||||
{::track/dispose {:id ::ledger-params}}))
|
||||
|
||||
(re-frame/reg-event-fx
|
||||
::mounted-pnl
|
||||
(fn [{:keys [db]} _]
|
||||
(let [qp (query-params)]
|
||||
{:db (forms/start-form db ::form {:periods (->> qp
|
||||
:periods
|
||||
(mapv (fn [period]
|
||||
{:start (str->date (:start period) standard)
|
||||
:end (str->date (:end period) standard)})))
|
||||
:clients (mapv (fn [c] {:client c :id (random-uuid)})
|
||||
(or (:clients qp)
|
||||
[(some-> @(re-frame/subscribe [::subs/client]) (select-keys [:name :id]) )]))
|
||||
:include-deltas false
|
||||
:show-advanced? false})
|
||||
::track/register {:id ::ledger-params
|
||||
:subscription [::data-page/params ::ledger]
|
||||
:event-fn (fn [params] [::ledger-params-change params])}})))
|
||||
|
||||
(defn ledger-list [_ ]
|
||||
[:div [:a.delete.is-pulled-right {:on-click (dispatch-event [::ledger-list-closing])}]
|
||||
[:div
|
||||
[:h1.title "Ledger entries"]
|
||||
[ledger-table/table {:id :ledger
|
||||
:data-page ::ledger}]]])
|
||||
|
||||
(defn cash-flows-page []
|
||||
(reagent/create-class
|
||||
{:display-name "profit-and-loss-page"
|
||||
:component-did-mount #(re-frame/dispatch [::mounted-pnl])
|
||||
:component-will-unmount #(re-frame/dispatch [::unmounted-pnl])
|
||||
:reagent-render
|
||||
(fn []
|
||||
(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"])))}))
|
||||
@@ -36,13 +36,6 @@
|
||||
|
||||
[:span {:class "icon icon-performance-increase-1" :style {:font-size "25px"}}]
|
||||
[:span {:class "name"} "Profit & Loss Detail"]]]
|
||||
(when (= "admin" (:user/role user))
|
||||
[:li.menu-item
|
||||
[:a.item {:href (bidi/path-for routes/routes :cash-flows)
|
||||
:class [(active-when ap = :cash-flows)]}
|
||||
|
||||
[:span {:class "icon icon-performance-increase-1" :style {:font-size "25px"}}]
|
||||
[:span {:class "name"} "Cash Flows"]]])
|
||||
[:li.menu-item
|
||||
[:a.item {:href (bidi/path-for routes/routes :balance-sheet)
|
||||
:class [(active-when ap = :balance-sheet)]}
|
||||
|
||||
Reference in New Issue
Block a user