beginning on new journal entry
This commit is contained in:
@@ -121,6 +121,7 @@ htmx.defineExtension('trigger-filter', {
|
|||||||
initDatepicker = function(elem) {
|
initDatepicker = function(elem) {
|
||||||
const modalParent = elem.closest('#modal-content');
|
const modalParent = elem.closest('#modal-content');
|
||||||
if (modalParent) {
|
if (modalParent) {
|
||||||
|
console.log('datepicker in modal')
|
||||||
return new Datepicker(elem, {format: "mm/dd/yyyy", autohide: true, container: "#modal-content .modal-card"});
|
return new Datepicker(elem, {format: "mm/dd/yyyy", autohide: true, container: "#modal-content .modal-card"});
|
||||||
} else {
|
} else {
|
||||||
return new Datepicker(elem, {format: "mm/dd/yyyy", autohide: true});
|
return new Datepicker(elem, {format: "mm/dd/yyyy", autohide: true});
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -23,6 +23,7 @@
|
|||||||
[auto-ap.ssr.ledger.common :refer [bank-account-filter default-read
|
[auto-ap.ssr.ledger.common :refer [bank-account-filter default-read
|
||||||
fetch-ids grid-page query-schema]]
|
fetch-ids grid-page query-schema]]
|
||||||
[auto-ap.ssr.ledger.investigate :as investigate]
|
[auto-ap.ssr.ledger.investigate :as investigate]
|
||||||
|
[auto-ap.ssr.ledger.new :as new]
|
||||||
[auto-ap.ssr.ledger.profit-and-loss :as profit-and-loss]
|
[auto-ap.ssr.ledger.profit-and-loss :as profit-and-loss]
|
||||||
[auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]]
|
[auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]]
|
||||||
[auto-ap.ssr.svg :as svg]
|
[auto-ap.ssr.svg :as svg]
|
||||||
@@ -732,4 +733,5 @@
|
|||||||
balance-sheet/key->handler
|
balance-sheet/key->handler
|
||||||
profit-and-loss/key->handler
|
profit-and-loss/key->handler
|
||||||
cash-flows/key->handler
|
cash-flows/key->handler
|
||||||
investigate/key->handler))
|
investigate/key->handler
|
||||||
|
new/key->handler))
|
||||||
@@ -417,13 +417,10 @@
|
|||||||
:parse-query-params (fn [p]
|
:parse-query-params (fn [p]
|
||||||
(mc/decode query-schema p main-transformer))
|
(mc/decode query-schema p main-transformer))
|
||||||
:action-buttons (fn [request]
|
:action-buttons (fn [request]
|
||||||
(let [[_ _ outstanding total] (:page-results request)]
|
[(com/button {:color :primary
|
||||||
[ #_(when (can? (:identity request) {:subject :invoice :activity :bulk-delete})
|
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||||
(com/button {:hx-get (str (bidi/path-for ssr-routes/only-routes ::route/bulk-delete))
|
::route/new)}
|
||||||
"x-bind:hx-vals" "JSON.stringify({selected: $data.selected, 'all-selected': $data.all_selected})"
|
"Add journal entry")])
|
||||||
"hx-include" "#ledger-filters"
|
|
||||||
:color :red}
|
|
||||||
"Void selected")) ]))
|
|
||||||
:row-buttons (fn [request entity]
|
:row-buttons (fn [request entity]
|
||||||
[(when (and (= :invoice-status/unpaid (:invoice/status entity))
|
[(when (and (= :invoice-status/unpaid (:invoice/status entity))
|
||||||
(can? (:identity request) {:subject :invoice :activity :delete}))
|
(can? (:identity request) {:subject :invoice :activity :delete}))
|
||||||
|
|||||||
217
src/clj/auto_ap/ssr/ledger/new.clj
Normal file
217
src/clj/auto_ap/ssr/ledger/new.clj
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
(ns auto-ap.ssr.ledger.new
|
||||||
|
(:require
|
||||||
|
[auto-ap.datomic :refer [conn pull-attr]]
|
||||||
|
[auto-ap.datomic.accounts :as d-accounts]
|
||||||
|
[auto-ap.permissions :refer [wrap-must]]
|
||||||
|
[auto-ap.routes.ledger :as route]
|
||||||
|
[auto-ap.routes.utils :refer [wrap-client-redirect-unauthenticated]]
|
||||||
|
[auto-ap.ssr-routes :as ssr-routes]
|
||||||
|
[auto-ap.ssr.common-handlers :refer [add-new-entity-handler]]
|
||||||
|
[auto-ap.ssr.components :as com]
|
||||||
|
[auto-ap.ssr.form-cursor :as fc]
|
||||||
|
[auto-ap.ssr.hx :as hx]
|
||||||
|
[auto-ap.ssr.nested-form-params :refer [wrap-nested-form-params]]
|
||||||
|
[auto-ap.ssr.svg :as svg]
|
||||||
|
[auto-ap.ssr.utils :refer [apply-middleware-to-all-handlers entity-id
|
||||||
|
modal-response wrap-schema-enforce]]
|
||||||
|
[auto-ap.time :as atime]
|
||||||
|
[bidi.bidi :as bidi]
|
||||||
|
[datomic.api :as dc]))
|
||||||
|
|
||||||
|
(defn- account-typeahead*
|
||||||
|
[{:keys [name value client-id x-model]}]
|
||||||
|
[:div.flex.flex-col
|
||||||
|
(com/typeahead {:name name
|
||||||
|
:placeholder "Search..."
|
||||||
|
:url (str (bidi/path-for ssr-routes/only-routes :account-search) "?client-id=" client-id)
|
||||||
|
:id name
|
||||||
|
:x-model x-model
|
||||||
|
:value value
|
||||||
|
:content-fn (fn [value]
|
||||||
|
(let [a (dc/pull (dc/db conn) d-accounts/default-read value)]
|
||||||
|
(when value
|
||||||
|
(str
|
||||||
|
(:account/numeric-code a)
|
||||||
|
" - "
|
||||||
|
(:account/name (d-accounts/clientize a
|
||||||
|
client-id))))))})])
|
||||||
|
|
||||||
|
(defn- location-select*
|
||||||
|
[{:keys [name account-location client-locations value]}]
|
||||||
|
(com/select {:options (into [["" ""]]
|
||||||
|
(cond account-location
|
||||||
|
[[account-location account-location]]
|
||||||
|
|
||||||
|
(seq client-locations)
|
||||||
|
(into [["Shared" "Shared"]]
|
||||||
|
(for [cl client-locations]
|
||||||
|
[cl cl]))
|
||||||
|
:else
|
||||||
|
[["Shared" "Shared"]]))
|
||||||
|
:name name
|
||||||
|
:value value
|
||||||
|
:class "w-full"}))
|
||||||
|
|
||||||
|
(defn- line-item-row*
|
||||||
|
[account client-id client-locations]
|
||||||
|
(com/data-grid-row
|
||||||
|
(-> {:x-data (hx/json {:accountId (or (:db/id (fc/field-value (:transaction-rule-account/account account)))
|
||||||
|
(fc/field-value (:transaction-rule-account/account account)))
|
||||||
|
:location (fc/field-value (:transaction-rule-account/location account))
|
||||||
|
:show (boolean (not (fc/field-value (:new? account))))})
|
||||||
|
:data-key "show"
|
||||||
|
:x-ref "p"}
|
||||||
|
hx/alpine-mount-then-appear)
|
||||||
|
(let [account-name (fc/field-name (:transaction-rule-account/account account))]
|
||||||
|
(list
|
||||||
|
|
||||||
|
(fc/with-field :db/id
|
||||||
|
(com/hidden {:name (fc/field-name)
|
||||||
|
:value (fc/field-value)}))
|
||||||
|
(fc/with-field :transaction-rule-account/account
|
||||||
|
(com/data-grid-cell
|
||||||
|
{}
|
||||||
|
(com/validated-field
|
||||||
|
{:errors (fc/field-errors)}
|
||||||
|
[:div {:hx-trigger "changed"
|
||||||
|
:hx-target "next div"
|
||||||
|
:hx-vals (format "js:{name: '%s', 'client-id': event.detail.clientId || '', value: event.detail.accountId || ''}" account-name)
|
||||||
|
:hx-get (str (bidi/path-for ssr-routes/only-routes ::route/account-typeahead))
|
||||||
|
:x-init "$watch('clientId', cid => $dispatch('changed', $data));"}]
|
||||||
|
(account-typeahead* {:value (fc/field-value)
|
||||||
|
:client-id client-id
|
||||||
|
:name (fc/field-name)
|
||||||
|
:x-model "accountId"}))))
|
||||||
|
(fc/with-field :transaction-rule-account/location
|
||||||
|
(com/data-grid-cell
|
||||||
|
{}
|
||||||
|
(com/validated-field
|
||||||
|
{:errors (fc/field-errors)
|
||||||
|
:x-data (hx/json {:location (fc/field-value)})}
|
||||||
|
;; TODO make this thing into a component
|
||||||
|
[:div {:hx-trigger "changed"
|
||||||
|
:hx-target "next *"
|
||||||
|
:hx-swap "outerHTML"
|
||||||
|
:hx-vals (format "js:{name: '%s', 'client-id': event.detail.clientId || '', 'account-id': event.detail.accountId || '', value: event.detail.location || ''}" (fc/field-name))
|
||||||
|
:hx-get (bidi/path-for ssr-routes/only-routes ::route/location-select)
|
||||||
|
:x-init "$watch('clientId', cid => $dispatch('changed', $data)); $watch('accountId', cid => $dispatch('changed', $data) )"}]
|
||||||
|
(location-select* {:name (fc/field-name)
|
||||||
|
:account-location (:account/location (cond->> (:transaction-rule-account/account @account)
|
||||||
|
(nat-int? (:transaction-rule-account/account @account)) (dc/pull (dc/db conn)
|
||||||
|
'[:account/location])))
|
||||||
|
:client-locations client-locations
|
||||||
|
:x-model "location"
|
||||||
|
:value (fc/field-value)}))))
|
||||||
|
(fc/with-field :transaction-rule-account/percentage
|
||||||
|
(com/data-grid-cell
|
||||||
|
{}
|
||||||
|
(com/validated-field
|
||||||
|
{:errors (fc/field-errors)}
|
||||||
|
(com/money-input {:name (fc/field-name)
|
||||||
|
:class "w-16"
|
||||||
|
:value (some-> (fc/field-value)
|
||||||
|
(* 100)
|
||||||
|
(long))}))))))
|
||||||
|
(com/data-grid-cell {:class "align-top"}
|
||||||
|
(com/a-icon-button {"@click.prevent.stop" "show=false; setTimeout(() => $refs.p.remove(), 500)"} svg/x))))
|
||||||
|
|
||||||
|
(defn form* [request]
|
||||||
|
(fc/start-form (:form-params request)
|
||||||
|
(:form-errors request)
|
||||||
|
[:div.flex.gap-4.flex-col
|
||||||
|
(fc/with-field :journal-entry/client
|
||||||
|
(com/validated-field
|
||||||
|
{:label "Client"
|
||||||
|
:errors (fc/field-errors)}
|
||||||
|
[:div.w-96
|
||||||
|
(com/typeahead {:name (fc/field-name)
|
||||||
|
:error? (fc/error?)
|
||||||
|
:class "w-96"
|
||||||
|
:placeholder "Search..."
|
||||||
|
:url (bidi/path-for ssr-routes/only-routes :company-search)
|
||||||
|
:value (fc/field-value)
|
||||||
|
:content-fn (fn [c] (pull-attr (dc/db conn) :client/name c))})]))
|
||||||
|
(fc/with-field :invoice/date
|
||||||
|
(com/validated-field
|
||||||
|
{:label "Date"
|
||||||
|
:errors (fc/field-errors)}
|
||||||
|
[:div {:class "w-24"}
|
||||||
|
(com/date-input {:value (some-> (fc/field-value)
|
||||||
|
(atime/unparse-local atime/normal-date))
|
||||||
|
:name (fc/field-name)
|
||||||
|
:error? (fc/field-errors)
|
||||||
|
:placeholder "1/1/2024"})]))
|
||||||
|
(fc/with-field :journal-entry/vendor
|
||||||
|
(com/validated-field
|
||||||
|
{:label "Vendor"
|
||||||
|
:errors (fc/field-errors)}
|
||||||
|
[:div.w-96
|
||||||
|
(com/typeahead {:name (fc/field-name)
|
||||||
|
:error? (fc/error?)
|
||||||
|
:disabled (boolean (-> request :multi-form-state :snapshot :db/id))
|
||||||
|
:class "w-96"
|
||||||
|
:placeholder "Search..."
|
||||||
|
:url (bidi/path-for ssr-routes/only-routes :vendor-search)
|
||||||
|
:value (fc/field-value)
|
||||||
|
:content-fn (fn [c] (pull-attr (dc/db conn) :vendor/name c))})]))
|
||||||
|
(fc/with-field :invoice/total
|
||||||
|
(com/validated-field
|
||||||
|
{:label "Total"
|
||||||
|
:errors (fc/field-errors)}
|
||||||
|
[:div {:class "w-16"}
|
||||||
|
(com/money-input {:value (-> (fc/field-value))
|
||||||
|
:name (fc/field-name)
|
||||||
|
:class "w-24"
|
||||||
|
:error? (fc/field-errors)
|
||||||
|
:placeholder "212.44"})]))
|
||||||
|
(fc/with-field :journal-entry/line-items
|
||||||
|
(com/validated-field
|
||||||
|
{:errors (fc/field-errors)}
|
||||||
|
(let [client-locations (some->> (fc/field-value) :transaction-rule/client (pull-attr (dc/db conn) :client/locations))]
|
||||||
|
(com/data-grid {:headers [(com/data-grid-header {} "Account")
|
||||||
|
(com/data-grid-header {:class "w-32"} "Location")
|
||||||
|
(com/data-grid-header {:class "w-16"} "%")
|
||||||
|
(com/data-grid-header {:class "w-16"})]}
|
||||||
|
(fc/cursor-map #(line-item-row* % (:transaction-rule/client (fc/field-value)) client-locations))
|
||||||
|
(com/data-grid-new-row {:colspan 4
|
||||||
|
:hx-get (bidi/path-for ssr-routes/only-routes
|
||||||
|
::route/new-line-item)
|
||||||
|
:index (count (fc/field-value))
|
||||||
|
:tr-params (hx/bind-alpine-vals {} {"client-id" "clientId"})}
|
||||||
|
"New account")))))]))
|
||||||
|
|
||||||
|
|
||||||
|
(defn new [request]
|
||||||
|
(modal-response
|
||||||
|
(com/modal {}
|
||||||
|
(com/modal-card {:class ""}
|
||||||
|
[:div "New ledger entry"]
|
||||||
|
[:div (form* request)]
|
||||||
|
[:div (com/button {:color :primary} "Save")]))
|
||||||
|
#_[:div]))
|
||||||
|
|
||||||
|
(def key->handler
|
||||||
|
(apply-middleware-to-all-handlers
|
||||||
|
(->
|
||||||
|
{::route/new (-> new
|
||||||
|
#_(wrap-schema-enforce :query-schema query-schema)
|
||||||
|
#_(wrap-form-4xx-2 profit-and-loss))
|
||||||
|
::route/new-line-item
|
||||||
|
(-> (add-new-entity-handler [:journal-entry/line-items]
|
||||||
|
(fn render [cursor request]
|
||||||
|
(line-item-row*
|
||||||
|
cursor
|
||||||
|
(:client-id (:query-params request))
|
||||||
|
(some->> (:client-id (:query-params request)) (pull-attr (dc/db conn) :client/locations))))
|
||||||
|
(fn build-new-row [base _]
|
||||||
|
(assoc base :transaction-rule-account/location "Shared")))
|
||||||
|
(wrap-schema-enforce :query-schema [:map
|
||||||
|
[:client-id {:optional true}
|
||||||
|
[:maybe entity-id]]]))})
|
||||||
|
|
||||||
|
(fn [h]
|
||||||
|
(-> h
|
||||||
|
#_(wrap-merge-prior-hx)
|
||||||
|
(wrap-must {:activity :edit :subject :ledger})
|
||||||
|
(wrap-nested-form-params)
|
||||||
|
(wrap-client-redirect-unauthenticated)))))
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
(ns auto-ap.routes.ledger)
|
(ns auto-ap.routes.ledger)
|
||||||
|
|
||||||
(def routes {"" {:get ::all-page}
|
(def routes {"" {:get ::all-page}
|
||||||
|
"/new" {:get ::new
|
||||||
|
"/line-item" {:get ::new-line-item}}
|
||||||
|
|
||||||
"/external-new" ::external-page
|
"/external-new" ::external-page
|
||||||
"/external-import-new" {"" ::external-import-page
|
"/external-import-new" {"" ::external-import-page
|
||||||
"/parse" ::external-import-parse
|
"/parse" ::external-import-parse
|
||||||
|
|||||||
Reference in New Issue
Block a user