- Add vendor-changed HTMX handlers for both bulk code and individual edit - Pre-populate default account at 100% when vendor is selected and no accounts exist - Fix render-accounts-section to render from step-params correctly - Change bulk code vendor-changed from hx-get to hx-post to include form data - Add routes for vendor-changed endpoints - Update e2e tests to cover vendor pre-population - Run lein cljfmt fix across codebase
115 lines
4.6 KiB
Clojure
115 lines
4.6 KiB
Clojure
(ns iol-ion.tx.upsert-entity
|
|
(:require [datomic.api :as dc]
|
|
;; [clj-time.core :as time]
|
|
;; [clj-time.coerce :as coerce]
|
|
)
|
|
(:import [java.util UUID]))
|
|
|
|
(defn -random-tempid []
|
|
(str (UUID/randomUUID)))
|
|
|
|
(defn -by
|
|
[f fv xs]
|
|
(reduce
|
|
#(assoc %1 (f %2) (fv %2))
|
|
{}
|
|
xs))
|
|
|
|
(defn -pull-many [db read ids]
|
|
(->> (dc/q '[:find (pull ?e r)
|
|
:in $ [?e ...] r]
|
|
db
|
|
ids
|
|
read)
|
|
(map first)))
|
|
|
|
;; TODO add DATOMIC_EXT_CLASSPATH ala https://docs.datomic.com/pro/reference/database-functions.html#transaction-functions
|
|
;; (defn transform-common [v]
|
|
;; (cond
|
|
;; (nil? v)
|
|
;; v
|
|
|
|
;; (satisfies? clj-time.core/DateTimeProtocol v)
|
|
;; (clj-time.coerce/to-date v)
|
|
|
|
;; :else
|
|
;; v))
|
|
|
|
(defn upsert-entity [db entity]
|
|
(when-not (or (:db/id entity)
|
|
(:db/ident entity))
|
|
(datomic.api/cancel {:cognitect.anomalies/category :cognitect.anomalies/incorrect
|
|
:cognitect.anomalies/message
|
|
(str "Cannot upsert without :db/id or :db/ident, " entity)}))
|
|
(let [e (or (:db/id entity) (:db/ident entity))
|
|
is-new? (string? e)
|
|
extant-entity (when-not is-new?
|
|
(dc/pull db (keys entity) (or (:db/id entity) (:db/ident entity))))
|
|
ident->value-type (-by :db/ident (comp :db/ident
|
|
:db/valueType)
|
|
(-pull-many
|
|
db
|
|
[{:db/valueType [:db/ident]} :db/ident]
|
|
(keys entity)))
|
|
|
|
ident->cardinality (-by :db/ident (comp :db/ident
|
|
:db/cardinality)
|
|
(-pull-many
|
|
db
|
|
[{:db/cardinality [:db/ident]} :db/ident]
|
|
(keys entity)))
|
|
ops (->> entity
|
|
(reduce
|
|
(fn [ops [a v]]
|
|
(cond
|
|
(= :db/id a)
|
|
ops
|
|
|
|
(= :db/ident a)
|
|
ops
|
|
|
|
(or (= v (a extant-entity))
|
|
(= v (:db/ident (a extant-entity) :nope))
|
|
(= v (:db/id (a extant-entity)) :nope))
|
|
ops
|
|
|
|
(and (nil? v)
|
|
(not (nil? (a extant-entity))))
|
|
(if (= :db.cardinality/many (ident->cardinality a))
|
|
(into ops (map (fn [v]
|
|
[:db/retract e a (cond-> v
|
|
(:db/id v) :db/id)])
|
|
(a extant-entity)))
|
|
|
|
(conj ops [:db/retract e a (cond-> (a extant-entity)
|
|
(:db/id (a extant-entity)) :db/id)]))
|
|
|
|
(nil? v)
|
|
ops
|
|
|
|
;; reset relationships if it's refs, and not a lookup (i.e., seq of maps, or empty seq)
|
|
|
|
(and (sequential? v) (= :db.type/tuple (ident->value-type a)) (not (= :db.cardinality/many (ident->cardinality a))))
|
|
(conj ops [:db/add e a v])
|
|
|
|
(and (sequential? v) (= :db.type/ref (ident->value-type a)) (every? map? v))
|
|
(into ops [[:reset-rels e a v]])
|
|
|
|
(= :db.cardinality/many (ident->cardinality a))
|
|
(into ops [[:reset-scalars e a v]])
|
|
|
|
(and (sequential? v) (not= :db.type/ref (ident->value-type a)))
|
|
(into ops [[:reset-scalars e a v]])
|
|
|
|
(and (map? v)
|
|
(= :db.type/ref (ident->value-type a)))
|
|
(let [id (or (:db/id v) (-random-tempid))]
|
|
(-> ops
|
|
(conj [:db/add e a id])
|
|
(into [[:upsert-entity (assoc v :db/id id)]])))
|
|
|
|
:else
|
|
(conj ops [:db/add e a v])))
|
|
[]))]
|
|
ops))
|