Skip to content

Commit

Permalink
Merge pull request #1564 from Opetushallitus/OK-553-application-unpai…
Browse files Browse the repository at this point in the history
…d-state

OK-553 hakemusmaksu payment states
  • Loading branch information
vaeinoe committed Jun 25, 2024
2 parents 2beb2ff + 86a0813 commit c1098d6
Show file tree
Hide file tree
Showing 6 changed files with 334 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
CREATE TABLE kk_application_payment_states
(
id bigserial PRIMARY KEY,
person_oid text NOT NULL,
start_term text NOT NULL,
start_year smallint NOT NULL,
state text NOT NULL,
created_time timestamp with time zone DEFAULT now(),
modified_time timestamp with time zone DEFAULT now(),
UNIQUE(person_oid, start_term, start_year)
);

COMMENT ON TABLE kk_application_payment_states IS 'Korkeakoulujen hakemusmaksujen tila aloituslukukausittain';

CREATE TABLE kk_application_payment_events
(
id bigserial PRIMARY KEY,
kk_application_payment_state_id bigint REFERENCES kk_application_payment_states (id),
new_state text,
event_type text NOT NULL,
virkailija_oid text REFERENCES virkailija(oid),
message text,
created_time timestamp with time zone DEFAULT now()
);

COMMENT ON TABLE kk_application_payment_events IS 'Korkeakoulujen hakemusmaksujen tilamuutoshistoria';
33 changes: 33 additions & 0 deletions resources/sql/kk-application-payment-queries.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
-- name: yesql-get-kk-application-payment-states-for-person-oids
SELECT
id,
person_oid,
start_term,
start_year,
state,
created_time,
modified_time
FROM kk_application_payment_states
WHERE person_oid IN (:person_oids) AND start_term = :start_term AND start_year = :start_year;

-- name: yesql-upsert-kk-application-payment-state<!
INSERT INTO kk_application_payment_states (person_oid, start_term, start_year, state, created_time, modified_time)
VALUES (:person_oid, :start_term, :start_year, :state, now(), now())
ON CONFLICT (person_oid, start_term, start_year)
DO UPDATE SET state = :state, modified_time = now();

-- name: yesql-add-kk-application-payment-event<!
INSERT INTO kk_application_payment_events (kk_application_payment_state_id, new_state, event_type, virkailija_oid, message)
VALUES (:kk_application_payment_state_id, :new_state, :event_type, :virkailija_oid, :message);

-- name: yesql-get-kk-application-payment-events
SELECT
id,
kk_application_payment_state_id,
new_state,
event_type,
virkailija_oid,
message,
created_time
FROM kk_application_payment_events
WHERE kk_application_payment_state_id IN (:kk_application_payment_state_ids);
91 changes: 91 additions & 0 deletions spec/ataru/kk_application_payment/kk_application_payment_spec.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
(ns ataru.kk-application-payment.kk-application-payment-spec
(:require [speclj.core :refer [describe tags it should-throw should= before-all]]
[ataru.kk-application-payment.kk-application-payment :as payment]
[clojure.java.jdbc :as jdbc]
[ataru.db.db :as db]))

(defn- delete-states-and-events! []
(jdbc/with-db-transaction [conn {:datasource (db/get-datasource :db)}]
(jdbc/delete! conn :kk_application_payment_events [])
(jdbc/delete! conn :kk_application_payment_states [])))

(def test-term-spring "kausi_k")
(def test-term-fall "kausi_s")
(def test-term-error "kausi_a")
(def test-year-ok 2025)
(def test-year-error 2024)
(def test-state-pending "payment-pending")
(def test-state-not-required "payment-not-required")
(def test-state-paid "payment-paid")
(def test-event-updated "state-updated")

(describe "application payment states"
(tags :unit :kk-application-payment)

(before-all
(delete-states-and-events!))

(describe "payment state validation"
(it "should not allow setting fee for spring 2025 (starts from fall 2025)"
(should-throw (payment/set-application-fee-required
"1.2.3.4.5.6" test-term-spring test-year-ok nil nil)))

(it "should not allow setting fee for year earlier than 2025"
(should-throw (payment/set-application-fee-required
"1.2.3.4.5.6" test-term-spring test-year-error nil nil)))

(it "should not allow setting fee for invalid term"
(should-throw (payment/set-application-fee-required
"1.2.3.4.5.6" test-term-error test-year-ok nil nil))))

(describe "payment state setting"
(it "should set and get application fee required for a person with oid"
(let [oid "1.2.3.4.5.6"
state-id (payment/set-application-fee-required
oid test-term-fall test-year-ok nil nil)
states (payment/get-payment-states [oid] test-term-fall test-year-ok)
events (payment/get-payment-events [state-id])
state (first states)
event (first events)]
(should= 1 (count states))
(should= 1 (count events))
(should= {:id state-id, :person_oid oid, :start_term test-term-fall,
:start_year test-year-ok, :state test-state-pending}
(dissoc state :created_time :modified_time))
(should= {:kk_application_payment_state_id state-id, :new_state test-state-pending,
:event_type test-event-updated, :virkailija_oid nil, :message nil}
(dissoc event :id :created_time))))

(it "should set and get application fee not required for a person with oid"
(let [oid "1.2.3.4.5.7"
state-id (payment/set-application-fee-not-required
oid test-term-fall test-year-ok nil nil)
states (payment/get-payment-states [oid] test-term-fall test-year-ok)
events (payment/get-payment-events [state-id])
state (first states)
event (first events)]
(should= 1 (count states))
(should= 1 (count events))
(should= {:id state-id, :person_oid oid, :start_term test-term-fall,
:start_year test-year-ok, :state test-state-not-required}
(dissoc state :created_time :modified_time))
(should= {:kk_application_payment_state_id state-id, :new_state test-state-not-required,
:event_type test-event-updated, :virkailija_oid nil, :message nil}
(dissoc event :id :created_time))))

(it "should set and get application fee paid for a person with oid"
(let [oid "1.2.3.4.5.8"
state-id (payment/set-application-fee-paid
oid test-term-fall test-year-ok nil nil)
states (payment/get-payment-states [oid] test-term-fall test-year-ok)
events (payment/get-payment-events [state-id])
state (first states)
event (first events)]
(should= 1 (count states))
(should= 1 (count events))
(should= {:id state-id, :person_oid oid, :start_term test-term-fall,
:start_year test-year-ok, :state test-state-paid}
(dissoc state :created_time :modified_time))
(should= {:kk_application_payment_state_id state-id, :new_state test-state-paid,
:event_type test-event-updated, :virkailija_oid nil, :message nil}
(dissoc event :id :created_time))))))
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
(ns ataru.kk-application-payment.kk-application-payment-store-spec
(:require [speclj.core :refer [describe tags it should-not-be-nil should= should-not before-all]]
[ataru.kk-application-payment.kk-application-payment-store :as store]
[ataru.db.db :as db]
[clojure.java.jdbc :as jdbc]))

(def test-person-oid "1.2.3.4.5.6")
(def test-term-spring "kausi_k")
(def test-term-fall "kausi_s")
(def test-year 2025)
(def test-year-2 2026)
(def test-state-pending "payment-pending")
(def test-state-paid "payment-paid")
(def test-event-updated "state-updated")
(def test-event-comment "comment")

(defn- delete-states-and-events! []
(jdbc/with-db-transaction [conn {:datasource (db/get-datasource :db)}]
(jdbc/delete! conn :kk_application_payment_events [])
(jdbc/delete! conn :kk_application_payment_states [])))

(describe "kk application payment states"
(tags :unit :kk-application-payment)

(before-all
(delete-states-and-events!))

(it "should store payment states for person and terms"
(let [spring-state (store/create-or-update-kk-application-payment-state!
test-person-oid test-term-spring test-year test-state-pending)
fall-state (store/create-or-update-kk-application-payment-state!
test-person-oid test-term-fall test-year test-state-paid)]
(should-not-be-nil (:id spring-state))
(should-not-be-nil (:id fall-state))
(should-not (= (:id spring-state) (:id fall-state)))))

(it "should update a payment state for person"
(let [new-state (store/create-or-update-kk-application-payment-state!
test-person-oid test-term-spring test-year test-state-pending)
updated-state (store/create-or-update-kk-application-payment-state!
test-person-oid test-term-spring test-year test-state-paid)]
(should= (:id new-state) (:id updated-state))))

(it "should get payment states for person"
(let [spring-state-id (:id (store/create-or-update-kk-application-payment-state!
test-person-oid test-term-spring test-year test-state-pending))
fall-state-id (:id (store/create-or-update-kk-application-payment-state!
test-person-oid test-term-fall test-year test-state-paid))
spring-state (first (store/get-kk-application-payment-states [test-person-oid] test-term-spring test-year))
fall-state (first (store/get-kk-application-payment-states [test-person-oid] test-term-fall test-year))
expected-spring-state {:id spring-state-id :person_oid test-person-oid :start_term test-term-spring
:start_year test-year :state test-state-pending}
expected-fall-state {:id fall-state-id :person_oid test-person-oid :start_term test-term-fall
:start_year test-year :state test-state-paid}]
(should= expected-spring-state (dissoc spring-state :created_time :modified_time))
(should= expected-fall-state (dissoc fall-state :created_time :modified_time)))))

(describe "kk application payment events"
(tags :unit :kk-application-payment)

(before-all
(delete-states-and-events!))

(it "should store and retrieve payment events for payment state"
(let [state-id (:id (store/create-or-update-kk-application-payment-state!
test-person-oid test-term-spring test-year-2 test-state-pending))
event-1-id (store/create-kk-application-payment-event!
state-id test-state-paid test-event-updated nil nil)
event-2-id (store/create-kk-application-payment-event!
state-id nil test-event-comment nil "Kommentti")
events (store/get-kk-application-payment-events [state-id])]
(should-not-be-nil event-1-id)
(should-not-be-nil event-2-id)
(should-not (= event-1-id event-2-id))
(should= 2 (count events)))))
69 changes: 69 additions & 0 deletions src/clj/ataru/kk_application_payment/kk_application_payment.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
(ns ataru.kk-application-payment.kk-application-payment
"Logic related to kk application processing payments, AKA hakemusmaksu. The basic spec is that
non-exempt non-EU native applicants should be charged an application fee once per semester.
NB! Semester is defined here by the start date of the actual first higher education semester,
not the application date."
(:require [ataru.kk-application-payment.kk-application-payment-store :as store]
[taoensso.timbre :as log]))

; TODO: application payments should be only charged for hakus starting on or after 1.1.2025 -> scoped to OK-556

(def application-payment-start-year
"Application payments are charged from studies starting on 2025 or later."
2025)

(def valid-terms
"Semesters / terms for kk application payments: one payment is required per starting term."
#{:kausi_k :kausi_s})

(def valid-payment-states
#{:payment-not-required
:payment-pending
:payment-paid})

(def valid-event-types
#{:state-updated})

(defn start-term-valid?
"Payments are only ever charged from Autumn 2025 onwards"
[term year]
(let [term-kw (keyword term)]
(or
(and (contains? valid-terms term-kw) (> year application-payment-start-year))
(and (= term-kw :kausi_s) (= year application-payment-start-year) ))))

(defn- set-payment-state
[person-oid term year new-state virkailija-oid message]
(if (and (contains? valid-payment-states (keyword new-state))
(start-term-valid? term year))
(let [state-id (:id (store/create-or-update-kk-application-payment-state!
person-oid term year new-state))]
(store/create-kk-application-payment-event! state-id new-state "state-updated" virkailija-oid message)
(log/info
(str "Set payment state of person " person-oid " for term " term " " year " to " new-state))
state-id)
(throw (ex-info "Parameter validation failed while setting payment state"
{:person-oid person-oid :term term :year year :state new-state}))))

(defn get-payment-states
[person-oids term year]
(store/get-kk-application-payment-states person-oids term year))

(defn get-payment-events
[state-ids]
(store/get-kk-application-payment-events state-ids))

(defn set-application-fee-required
"Sets kk processing fee required for the target term."
[person-oid term year virkailija-oid message]
(set-payment-state person-oid term year "payment-pending" virkailija-oid message))

(defn set-application-fee-not-required
"Sets kk processing fee required for the target term."
[person-oid term year virkailija-oid message]
(set-payment-state person-oid term year "payment-not-required" virkailija-oid message))

(defn set-application-fee-paid
"Sets kk processing fee paid for the target term."
[person-oid term year virkailija-oid message]
(set-payment-state person-oid term year "payment-paid" virkailija-oid message))
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
(ns ataru.kk-application-payment.kk-application-payment-store
(:require [ataru.db.db :as db]
[yesql.core :refer [defqueries]]))

(defqueries "sql/kk-application-payment-queries.sql")

(declare yesql-get-kk-application-payment-states-for-person-oids)
(declare yesql-upsert-kk-application-payment-state<!)
(declare yesql-add-kk-application-payment-event<!)
(declare yesql-get-kk-application-payment-events)

(defn- exec-db
[ds-key query params]
(db/exec ds-key query params))

(defn create-or-update-kk-application-payment-state!
[person-oid, start-term, start-year, state]
(exec-db :db yesql-upsert-kk-application-payment-state<! {:person_oid person-oid
:start_term start-term
:start_year start-year
:state state}))

(defn get-kk-application-payment-states
[person-oids start-term start-year]
(exec-db :db yesql-get-kk-application-payment-states-for-person-oids {:person_oids person-oids
:start_term start-term
:start_year start-year}))

(defn create-kk-application-payment-event!
[payment-state-id, new-state, event-type, virkailija-oid, message]
(exec-db :db yesql-add-kk-application-payment-event<! {:kk_application_payment_state_id payment-state-id
:new_state new-state
:event_type event-type
:virkailija_oid virkailija-oid
:message message}))

(defn get-kk-application-payment-events
[payment-state-ids]
(exec-db :db yesql-get-kk-application-payment-events
{:kk_application_payment_state_ids payment-state-ids}))

0 comments on commit c1098d6

Please sign in to comment.