Merge branch 'cash-flow-idea'
This commit is contained in:
@@ -62,6 +62,16 @@
|
|||||||
'[(<= ?date ?end-date)]]}
|
'[(<= ?date ?end-date)]]}
|
||||||
:args [(c/to-date (:end (:date-range args)))]})
|
:args [(c/to-date (:end (:date-range args)))]})
|
||||||
|
|
||||||
|
(:start (:due-range args)) (merge-query {:query {:in '[?start-due]
|
||||||
|
:where ['[?e :invoice/due ?due]
|
||||||
|
'[(>= ?due ?start-due)]]}
|
||||||
|
:args [(c/to-date (:start (:due-range args)))]})
|
||||||
|
|
||||||
|
(:end (:due-range args)) (merge-query {:query {:in '[?end-due]
|
||||||
|
:where ['[?e :invoice/due ?due]
|
||||||
|
'[(<= ?due ?end-due)]]}
|
||||||
|
:args [(c/to-date (:end (:due-range args)))]})
|
||||||
|
|
||||||
(:import-status args)
|
(:import-status args)
|
||||||
(merge-query {:query {:in ['?import-status]
|
(merge-query {:query {:in ['?import-status]
|
||||||
:where ['[?e :invoice/import-status ?import-status]]}
|
:where ['[?e :invoice/import-status ?import-status]]}
|
||||||
|
|||||||
@@ -394,6 +394,7 @@
|
|||||||
:invoice_page {:type '(list :invoice_page)
|
:invoice_page {:type '(list :invoice_page)
|
||||||
:args {:import_status {:type 'String}
|
:args {:import_status {:type 'String}
|
||||||
:date_range {:type :date_range}
|
:date_range {:type :date_range}
|
||||||
|
:due_range {:type :date_range}
|
||||||
:status {:type :invoice_status}
|
:status {:type :invoice_status}
|
||||||
:client_id {:type :id}
|
:client_id {:type :id}
|
||||||
:vendor_id {:type :id}
|
:vendor_id {:type :id}
|
||||||
@@ -871,11 +872,10 @@
|
|||||||
true (merge-query {:query {:where ['[?i :invoice/client ?c]
|
true (merge-query {:query {:where ['[?i :invoice/client ?c]
|
||||||
'[?i :invoice/outstanding-balance ?outstanding-balance]
|
'[?i :invoice/outstanding-balance ?outstanding-balance]
|
||||||
'[?i :invoice/total ?total]
|
'[?i :invoice/total ?total]
|
||||||
'[?i :invoice/date ?date]
|
'[?i :invoice/due ?date]
|
||||||
'[(.toInstant ^java.util.Date ?date) ?d2]
|
'[(.toInstant ^java.util.Date ?date) ?d2]
|
||||||
'[(.between java.time.temporal.ChronoUnit/DAYS (java.time.Instant/now) ?d2 ) ?d3]
|
'[(.between java.time.temporal.ChronoUnit/DAYS (java.time.Instant/now) ?d2 ) ?d3]
|
||||||
'[(+ 30 ?d3) ?d4]
|
'[(auto-ap.graphql/categorize ?d3) ?name]]}})
|
||||||
'[(auto-ap.graphql/categorize ?d4) ?name]]}})
|
|
||||||
|
|
||||||
true (d/query ))
|
true (d/query ))
|
||||||
result (group-by first result)]
|
result (group-by first result)]
|
||||||
|
|||||||
@@ -226,74 +226,8 @@
|
|||||||
rows)]
|
rows)]
|
||||||
@(d/transact conn txes)))
|
@(d/transact conn txes)))
|
||||||
|
|
||||||
(defn cash-flow-simple []
|
|
||||||
(let [total-cash (reduce
|
|
||||||
(fn [total [credit debit]]
|
|
||||||
(- (+ total credit)
|
|
||||||
debit))
|
|
||||||
0.0
|
|
||||||
(d/query {:query {:find '[?debit ?credit]
|
|
||||||
:in '[$ ?client]
|
|
||||||
:where ['[?j :journal-entry/client ?c]
|
|
||||||
'[?c :client/code ?client]
|
|
||||||
'[?j :journal-entry/line-items ?je]
|
|
||||||
'[?je :journal-entry-line/account ?ba]
|
|
||||||
'[?ba :bank-account/type :bank-account-type/check]
|
|
||||||
'[(get-else $ ?je :journal-entry-line/debit 0.0) ?debit]
|
|
||||||
'[(get-else $ ?je :journal-entry-line/credit 0.0) ?credit]]}
|
|
||||||
:args [(d/db (d/connect uri)) "CBC"]}))
|
|
||||||
bills-due-soon (d/query {:query {:find '[?due ?outstanding]
|
|
||||||
:in '[$ ?client ?due-before]
|
|
||||||
:where ['[?i :invoice/client ?c]
|
|
||||||
'[?c :client/code ?client]
|
|
||||||
'[?i :invoice/status :invoice-status/unpaid]
|
|
||||||
'[?i :invoice/due ?due]
|
|
||||||
'[(<= ?due ?due-before)]
|
|
||||||
'[?i :invoice/outstanding-balance ?outstanding]]}
|
|
||||||
:args [(d/db (d/connect uri)) "CBC" (c/to-date (t/plus (auto-ap.time/local-now) (t/days 7)))]})
|
|
||||||
outstanding-checks (reduce
|
|
||||||
+
|
|
||||||
0.0
|
|
||||||
(map first (d/query {:query {:find '[?amount]
|
|
||||||
:in '[$ ?client ?due-before]
|
|
||||||
:where ['[?p :payment/client ?c]
|
|
||||||
'[?c :client/code ?client]
|
|
||||||
'[?p :payment/status :payment-status/pending]
|
|
||||||
'[?p :payment/amount ?amount]
|
|
||||||
'(or
|
|
||||||
[?p :payment/type :payment-type/debit]
|
|
||||||
[?p :payment/type :payment-type/check])]}
|
|
||||||
:args [(d/db (d/connect uri)) "CBC" (c/to-date (t/plus (auto-ap.time/local-now) (t/days 7)))]})))]
|
|
||||||
[total-cash bills-due-soon outstanding-checks])
|
|
||||||
|
|
||||||
|
|
||||||
#_(->> (d/query {:query {:find '[?account-type-ident ?date ?debit ?credit]
|
|
||||||
:in '[$ ?client]
|
|
||||||
:where ['[?j :journal-entry/line-items ?je]
|
|
||||||
'[?j :journal-entry/date ?date]
|
|
||||||
'[?je :journal-entry-line/account ?a]
|
|
||||||
'[(get-else $ ?je :journal-entry-line/debit 0.0) ?debit]
|
|
||||||
'[(get-else $ ?je :journal-entry-line/credit 0.0) ?credit]
|
|
||||||
'[?a :account/type ?account-type]
|
|
||||||
'[?account-type :db/ident ?account-type-ident]]}
|
|
||||||
:args [(d/db (d/connect uri)) "CBC"]})
|
|
||||||
|
|
||||||
(reduce
|
|
||||||
(fn [result [account-type date debit credit]]
|
|
||||||
(let [date (clj-time.coerce/from-date date)]
|
|
||||||
(let [year-month (str (clj-time.core/year date) "-" (clj-time.core/month date))]
|
|
||||||
(-> result
|
|
||||||
(update-in [year-month account-type :debit]
|
|
||||||
(fn [existing-debit]
|
|
||||||
(+ (or existing-debit 0.0)
|
|
||||||
debit)))
|
|
||||||
(update-in [year-month account-type :credit]
|
|
||||||
(fn [existing-credit]
|
|
||||||
(+ (or existing-credit 0.0)
|
|
||||||
credit)))
|
|
||||||
(update-in [year-month account-type :count] #(inc (or % 0)))))))
|
|
||||||
{})))
|
|
||||||
|
|
||||||
(defn attach-signature [client-code filename]
|
(defn attach-signature [client-code filename]
|
||||||
@(d/transact (d/connect uri)
|
@(d/transact (d/connect uri)
|
||||||
[{:db/id [:client/code client-code]
|
[{:db/id [:client/code client-code]
|
||||||
|
|||||||
@@ -17,6 +17,22 @@
|
|||||||
(fn [uri]
|
(fn [uri]
|
||||||
(pushy/set-token! p/history uri)))
|
(pushy/set-token! p/history uri)))
|
||||||
|
|
||||||
|
(re-frame/reg-fx
|
||||||
|
:set-uri-params
|
||||||
|
(fn [uri-params]
|
||||||
|
(pushy/set-token! p/history
|
||||||
|
(str (.-protocol (.-location js/window)) "//" (.-host (.-location js/window)) (.-pathname (.-location js/window))
|
||||||
|
"?"
|
||||||
|
(cemerick.url/map->query (into {} (filter (fn [[k v]] v) uri-params)))
|
||||||
|
#_(reduce-kv (fn [result k v]
|
||||||
|
(if v
|
||||||
|
(str (or result "?")
|
||||||
|
(when result "&")
|
||||||
|
(name k) "=" (js/encodeURI v))
|
||||||
|
result))
|
||||||
|
nil
|
||||||
|
uri-params)))))
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
:new-window
|
:new-window
|
||||||
(fn [url]
|
(fn [url]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
[auto-ap.routes :as routes]
|
[auto-ap.routes :as routes]
|
||||||
[auto-ap.effects :as effects]
|
[auto-ap.effects :as effects]
|
||||||
|
|
||||||
[auto-ap.utils :refer [by]]
|
[auto-ap.utils :refer [by]]
|
||||||
[venia.core :as v]
|
[venia.core :as v]
|
||||||
[bidi.bidi :as bidi]
|
[bidi.bidi :as bidi]
|
||||||
@@ -38,9 +39,7 @@
|
|||||||
{:db (assoc db/default-db
|
{:db (assoc db/default-db
|
||||||
:active-page handler
|
:active-page handler
|
||||||
:last-client-id (.getItem js/localStorage "last-client-id")
|
:last-client-id (.getItem js/localStorage "last-client-id")
|
||||||
:query-params (->> (:query (url (.-location js/window)))
|
:query-params (auto-ap.views.utils/query-params)
|
||||||
(map (fn [[k v]] [(keyword k) v]))
|
|
||||||
(into {}))
|
|
||||||
:user token)
|
:user token)
|
||||||
:graphql {:token token
|
:graphql {:token token
|
||||||
:query-obj {:venia/queries [[:client
|
:query-obj {:venia/queries [[:client
|
||||||
|
|||||||
@@ -11,9 +11,7 @@
|
|||||||
|
|
||||||
(defn- dispatch-route [matched-route]
|
(defn- dispatch-route [matched-route]
|
||||||
(println "Matched route" matched-route)
|
(println "Matched route" matched-route)
|
||||||
(re-frame/dispatch [:auto-ap.events/set-active-page (:handler matched-route) (->> (:query (url (.-location js/window)))
|
(re-frame/dispatch [:auto-ap.events/set-active-page (:handler matched-route) (auto-ap.views.utils/query-params)]))
|
||||||
(map (fn [[k v]] [(keyword k) v]))
|
|
||||||
(into {}))]))
|
|
||||||
|
|
||||||
|
|
||||||
(def history (pushy/pushy dispatch-route parse-url))
|
(def history (pushy/pushy dispatch-route parse-url))
|
||||||
|
|||||||
@@ -59,10 +59,17 @@
|
|||||||
:end]]]})
|
:end]]]})
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::table-params
|
::specific-table-params
|
||||||
(fn [db]
|
(fn [db]
|
||||||
(::table-params db)))
|
(::table-params db)))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
::table-params
|
||||||
|
:<- [::specific-table-params]
|
||||||
|
:<- [::subs/query-params]
|
||||||
|
(fn [[specific-table-params query-params]]
|
||||||
|
(merge (select-keys query-params #{:start :sort}) specific-table-params )))
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::params-changed
|
::params-changed
|
||||||
[(re-frame/path [::table-params])]
|
[(re-frame/path [::table-params])]
|
||||||
|
|||||||
@@ -9,21 +9,41 @@
|
|||||||
[auto-ap.routes :as routes]
|
[auto-ap.routes :as routes]
|
||||||
[auto-ap.views.components.date-range-filter :refer [date-range-filter]]
|
[auto-ap.views.components.date-range-filter :refer [date-range-filter]]
|
||||||
[auto-ap.views.components.typeahead :refer [typeahead-entity]]
|
[auto-ap.views.components.typeahead :refer [typeahead-entity]]
|
||||||
[auto-ap.views.utils :refer [active-when dispatch-event bind-field horizontal-field date->str str->date pretty standard]]
|
[auto-ap.views.utils :refer [active-when dispatch-event bind-field horizontal-field date->str str->date pretty standard query-params]]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
[auto-ap.events :as events]))
|
[auto-ap.events :as events]))
|
||||||
|
|
||||||
|
(re-frame/reg-sub
|
||||||
|
::specific-filters
|
||||||
|
(fn [db ]
|
||||||
|
(::filters db nil)))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::filters
|
::filters
|
||||||
(fn [db ]
|
:<- [::specific-filters]
|
||||||
(::filters db {})))
|
:<- [::subs/vendors-by-id]
|
||||||
|
:<- [::subs/query-params]
|
||||||
|
(fn [[specific-filters vendors-by-id query-params] ]
|
||||||
|
(let [url-filters (-> query-params
|
||||||
|
(select-keys #{:vendor-id
|
||||||
|
:date-range
|
||||||
|
:due-range
|
||||||
|
:invoice-number-like
|
||||||
|
:status}))
|
||||||
|
url-filters {:vendor (when-let [vendor-id (:vendor-id url-filters)]
|
||||||
|
{:id (str vendor-id)
|
||||||
|
:name (get-in vendors-by-id [(str vendor-id) :name] "Loading...")})
|
||||||
|
:date-range (:date-range url-filters)
|
||||||
|
:due-range (:due-range url-filters)
|
||||||
|
:invoice-number-like {:raw (:invoice-number-like url-filters)
|
||||||
|
:settled (:invoice-number-like url-filters)}}]
|
||||||
|
(merge url-filters specific-filters ))))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::filter
|
::filter
|
||||||
:<- [::filters]
|
:<- [::filters]
|
||||||
(fn [filters [_ which]]
|
(fn [filters [_ which]]
|
||||||
(filters which)))
|
(get filters which)))
|
||||||
|
|
||||||
(re-frame/reg-sub
|
(re-frame/reg-sub
|
||||||
::filter-params
|
::filter-params
|
||||||
@@ -32,12 +52,13 @@
|
|||||||
(fn [[filters ap]]
|
(fn [[filters ap]]
|
||||||
{:vendor-id (:id (:vendor filters))
|
{:vendor-id (:id (:vendor filters))
|
||||||
:date-range (:date-range filters)
|
:date-range (:date-range filters)
|
||||||
|
:due-range (:due-range filters)
|
||||||
:invoice-number-like (:settled (:invoice-number-like filters))
|
:invoice-number-like (:settled (:invoice-number-like filters))
|
||||||
:status (condp = ap
|
:status (condp = ap
|
||||||
:invoices nil
|
:invoices nil
|
||||||
:unpaid-invoices :unpaid
|
:unpaid-invoices :unpaid
|
||||||
:paid-invoices :paid
|
:paid-invoices :paid
|
||||||
:voided-invoices :voided)}))
|
:voided-invoices :voided)}))
|
||||||
|
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
@@ -125,6 +146,13 @@
|
|||||||
[date-range-filter
|
[date-range-filter
|
||||||
{:on-change-event [::filter-changed :date-range]
|
{:on-change-event [::filter-changed :date-range]
|
||||||
:value @(re-frame/subscribe [::filter :date-range])}]]
|
:value @(re-frame/subscribe [::filter :date-range])}]]
|
||||||
|
|
||||||
|
[:p.menu-label "Due Range"]
|
||||||
|
[:div
|
||||||
|
[date-range-filter
|
||||||
|
{:on-change-event [::filter-changed :due-range]
|
||||||
|
:value @(re-frame/subscribe [::filter :due-range])}]]
|
||||||
|
|
||||||
[:p.menu-label "Invoice #"]
|
[:p.menu-label "Invoice #"]
|
||||||
[:div
|
[:div
|
||||||
[invoice-number-filter]]])]))
|
[invoice-number-filter]]])]))
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
(ns auto-ap.views.pages.home
|
(ns auto-ap.views.pages.home
|
||||||
(:require [auto-ap.views.components.layouts :refer [side-bar-layout]]
|
(:require [auto-ap.views.components.layouts :refer [side-bar-layout]]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
|
[bidi.bidi :as bidi]
|
||||||
|
[auto-ap.routes :as routes]
|
||||||
[auto-ap.subs :as subs]
|
[auto-ap.subs :as subs]
|
||||||
[cljs-time.core :as t]
|
[cljs-time.core :as t]
|
||||||
[auto-ap.views.utils :refer [local-now date->str]]
|
[auto-ap.views.utils :refer [local-now date->str standard]]
|
||||||
[reagent.core :as r]))
|
[reagent.core :as r]
|
||||||
|
[pushy.core :as pushy]))
|
||||||
|
|
||||||
|
|
||||||
(def pie-chart (r/adapt-react-class js/Recharts.PieChart))
|
(def pie-chart (r/adapt-react-class js/Recharts.PieChart))
|
||||||
@@ -47,16 +50,21 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
(defn make-cash-flow-chart [{:keys [width height data] }]
|
(defn make-cash-flow-chart [{:keys [width height data] }]
|
||||||
(println data)
|
(let [redirect-fn (fn [x]
|
||||||
[bar-chart {:width width :height height :data data :fill "#FFFFFF" :stackOffset "sign"}
|
(pushy/set-token! auto-ap.history/history (str (bidi/path-for routes/routes :unpaid-invoices) "?" (get (js->clj x) "query-params")))
|
||||||
[tool-tip]
|
)]
|
||||||
[bar {:dataKey "effective-balance" :fill (get colors 1) :stackId "a" :name "Effective Balance"}]
|
[bar-chart {:width width :height height :data data :fill "#FFFFFF" :stackOffset "sign"}
|
||||||
[bar {:dataKey "outstanding-payments" :fill (get colors 0) :stackId "a" :name "Outstanding Payments"}]
|
[tool-tip]
|
||||||
[bar {:dataKey "invoices" :fill (get colors 3) :stackId "a" :name "Invoices"}]
|
[bar {:dataKey "effective-balance" :fill (get colors 1) :stackId "a" :name "Effective Balance"
|
||||||
|
:on-click redirect-fn}]
|
||||||
|
[bar {:dataKey "outstanding-payments" :fill (get colors 0) :stackId "a" :name "Outstanding Payments"
|
||||||
|
:on-click redirect-fn}]
|
||||||
|
[bar {:dataKey "invoices" :fill (get colors 3) :stackId "a" :name "Invoices"
|
||||||
|
:on-click redirect-fn}]
|
||||||
|
|
||||||
[x-axis {:dataKey "name"}]
|
[x-axis {:dataKey "name"}]
|
||||||
[y-axis]
|
[y-axis]
|
||||||
[legend]]
|
[legend]])
|
||||||
)
|
)
|
||||||
|
|
||||||
(re-frame/reg-event-db
|
(re-frame/reg-event-db
|
||||||
@@ -99,8 +107,6 @@
|
|||||||
(let [due (if (t/before? (:due invoice) (local-now))
|
(let [due (if (t/before? (:due invoice) (local-now))
|
||||||
(local-now)
|
(local-now)
|
||||||
(:due invoice))]
|
(:due invoice))]
|
||||||
(println due)
|
|
||||||
|
|
||||||
(update result (date->str due)
|
(update result (date->str due)
|
||||||
(fn [r] (+ (or r 0.0) (:outstanding-balance invoice))))))
|
(fn [r] (+ (or r 0.0) (:outstanding-balance invoice))))))
|
||||||
{}
|
{}
|
||||||
@@ -113,14 +119,18 @@
|
|||||||
(reduce
|
(reduce
|
||||||
(fn [[{:keys [effective-balance] } :as acc] day]
|
(fn [[{:keys [effective-balance] } :as acc] day]
|
||||||
(let [invoices-due-today (invoices-due-soon (date->str (t/plus start-date (t/days day))) 0.0)]
|
(let [invoices-due-today (invoices-due-soon (date->str (t/plus start-date (t/days day))) 0.0)]
|
||||||
(conj acc
|
(let [today (t/plus start-date (t/days day))]
|
||||||
{:name (date->str (t/plus start-date (t/days day)))
|
(conj acc
|
||||||
:effective-balance (- effective-balance invoices-due-today)
|
{:name (date->str today)
|
||||||
:invoices (- invoices-due-today)})))
|
:effective-balance (- effective-balance invoices-due-today)
|
||||||
|
:invoices (- invoices-due-today)
|
||||||
|
:query-params (cemerick.url/map->query {:due-range {:start (date->str today standard)
|
||||||
|
:end (date->str today standard)}})}))))
|
||||||
(list {:name (date->str start-date)
|
(list {:name (date->str start-date)
|
||||||
:effective-balance effective-balance
|
:effective-balance effective-balance
|
||||||
:invoices (- (invoices-due-soon (date->str start-date) 0.0))
|
:invoices (- (invoices-due-soon (date->str start-date) 0.0))
|
||||||
:outstanding-payments (- outstanding-payments)})
|
:outstanding-payments (- outstanding-payments)
|
||||||
|
:query-params (cemerick.url/map->query {:due-range {:end (date->str start-date standard)}})})
|
||||||
(range 1 7))))))
|
(range 1 7))))))
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
|
|||||||
@@ -98,14 +98,17 @@
|
|||||||
::params-change
|
::params-change
|
||||||
[with-user (re-frame/inject-cofx ::inject/sub [::params])]
|
[with-user (re-frame/inject-cofx ::inject/sub [::params])]
|
||||||
(fn [{::keys [params] :as cofx} _]
|
(fn [{::keys [params] :as cofx} _]
|
||||||
(println "params" params)
|
|
||||||
{:db (-> (:db cofx)
|
{:db (-> (:db cofx)
|
||||||
(assoc-in [:status :loading] true)
|
(assoc-in [:status :loading] true)
|
||||||
(assoc-in [::last-params] params))
|
(assoc-in [::last-params] params))
|
||||||
:graphql {:token (-> cofx :db :user)
|
:graphql {:token (-> cofx :db :user)
|
||||||
:query-obj (table/query params )
|
:query-obj (table/query params )
|
||||||
:on-success [::received]
|
:on-success [::received]
|
||||||
:on-error [::events/page-failed]}}))
|
:on-error [::events/page-failed]}
|
||||||
|
:set-uri-params (dissoc params
|
||||||
|
:status
|
||||||
|
:client-id
|
||||||
|
:import-status)}))
|
||||||
|
|
||||||
(re-frame/reg-event-fx
|
(re-frame/reg-event-fx
|
||||||
::unmounted
|
::unmounted
|
||||||
|
|||||||
@@ -320,3 +320,10 @@
|
|||||||
:before (fn [context]
|
:before (fn [context]
|
||||||
(-> context
|
(-> context
|
||||||
(assoc-in [:coeffects :user] (get-in context [:coeffects :db :user]))))))
|
(assoc-in [:coeffects :user] (get-in context [:coeffects :db :user]))))))
|
||||||
|
|
||||||
|
(defn query-params []
|
||||||
|
(reduce-kv
|
||||||
|
(fn [result k v]
|
||||||
|
(assoc result (keyword k) (cljs.tools.reader.edn/read-string v)))
|
||||||
|
{}
|
||||||
|
(:query (cemerick.url/url (.-location js/window)))))
|
||||||
|
|||||||
Reference in New Issue
Block a user