(ns auto-ap.company.expense-reports-test (:require [auto-ap.datomic :refer [conn]] [auto-ap.integration.util :refer [admin-token setup-test-data test-account test-client test-invoice test-vendor user-token wrap-setup]] [auto-ap.ssr.company.reports.expense :as expense-reports] [clj-time.core :as time] [clojure.string :as str] [clojure.test :refer [deftest is testing use-fixtures]] [datomic.api :as dc])) (use-fixtures :each wrap-setup) ;; ============================================================================ ;; Expense Reports - Chart Behaviors ;; ============================================================================ (deftest test-vendor-typeahead-filter (testing "Behavior 6.3: It should provide a vendor typeahead to filter expenses to a specific vendor" (let [{:strs [test-client-id]} (setup-test-data [(test-client :db/id "test-client-id" :client/code "TEST01")])] (let [response (expense-reports/expense-breakdown-card {:identity (user-token test-client-id) :clients [{:db/id test-client-id}] :client {:db/id test-client-id} :query-params {}})] (is (= 200 (:status response))) ;; Response should contain vendor typeahead with vendor search URL (is (re-find #"/vendor/search" (:body response))))))) (deftest test-expense-account-typeahead-filter (testing "Behavior 6.4: It should provide an expense account typeahead to filter to a specific account" (let [{:strs [test-client-id]} (setup-test-data [(test-client :db/id "test-client-id" :client/code "TEST01")])] (let [response (expense-reports/expense-breakdown-card {:identity (user-token test-client-id) :clients [{:db/id test-client-id}] :client {:db/id test-client-id} :query-params {}})] (is (= 200 (:status response))) ;; Response should contain account typeahead with account search URL (is (re-find #"/account/search" (:body response))))))) (deftest test-refresh-chart-on-filter-change (testing "Behavior 6.5: It should refresh the chart when filters change" (let [{:strs [test-client-id]} (setup-test-data [(test-client :db/id "test-client-id" :client/code "TEST01")])] (let [response (expense-reports/expense-breakdown-card {:identity (user-token test-client-id) :clients [{:db/id test-client-id}] :client {:db/id test-client-id} :query-params {}})] (is (= 200 (:status response))) ;; The form should have hx-get pointing to the breakdown card endpoint (is (re-find #"/company/reports/expense/card" (:body response))) ;; The form should trigger on change (is (re-find #"change" (:body response))) ;; The form should target the chart container (is (re-find #"expense-breakdown-report" (:body response))))))) (deftest test-default-65-days-last-8-weeks (testing "Behavior 6.6: It should default to last 65 days of data but display last 8 weeks" (let [{:strs [test-client-id test-vendor-id test-account-id]} (setup-test-data [(test-client :db/id "test-client-id" :client/code "TEST01")]) ;; Create invoices across the last 65 days now (time/now) days-ago-10 (time/minus now (time/days 10)) days-ago-50 (time/minus now (time/days 50)) days-ago-70 (time/minus now (time/days 70))] @(dc/transact conn [{:db/id "invoice-1" :invoice/client test-client-id :invoice/vendor test-vendor-id :invoice/date (clj-time.coerce/to-date days-ago-10) :invoice/status :invoice-status/unpaid :invoice/import-status :import-status/imported :invoice/total 100.0 :invoice/outstanding-balance 100.0 :invoice/invoice-number "INV-001" :invoice/expense-accounts [{:invoice-expense-account/account test-account-id :invoice-expense-account/amount 100.0 :invoice-expense-account/location "DT"}]} {:db/id "invoice-2" :invoice/client test-client-id :invoice/vendor test-vendor-id :invoice/date (clj-time.coerce/to-date days-ago-50) :invoice/status :invoice-status/unpaid :invoice/import-status :import-status/imported :invoice/total 200.0 :invoice/outstanding-balance 200.0 :invoice/invoice-number "INV-002" :invoice/expense-accounts [{:invoice-expense-account/account test-account-id :invoice-expense-account/amount 200.0 :invoice-expense-account/location "DT"}]} {:db/id "invoice-3" :invoice/client test-client-id :invoice/vendor test-vendor-id :invoice/date (clj-time.coerce/to-date days-ago-70) :invoice/status :invoice-status/unpaid :invoice/import-status :import-status/imported :invoice/total 300.0 :invoice/outstanding-balance 300.0 :invoice/invoice-number "INV-003" :invoice/expense-accounts [{:invoice-expense-account/account test-account-id :invoice-expense-account/amount 300.0 :invoice-expense-account/location "DT"}]}]) ;; The lookup function should include invoices from last 65 days (invoice-1 and invoice-2) ;; but not invoice-3 (70 days ago) (let [data (expense-reports/lookup-breakdown-data {:clients [{:db/id test-client-id}] :client {:db/id test-client-id} :query-params {}})] ;; Should include 2 invoices (10 days and 50 days ago) ;; Note: invoice-3 at 70 days should be excluded by default 65-day window (is (>= 2 (count data)))) ;; The card should mention "last 8 weeks" (let [response (expense-reports/expense-breakdown-card {:identity (user-token test-client-id) :clients [{:db/id test-client-id}] :client {:db/id test-client-id} :query-params {}})] (is (re-find #"last 8 weeks" (:body response))))))) ;; ============================================================================ ;; Invoice Totals Behaviors ;; ============================================================================ (deftest test-default-date-range-last-30-days (testing "Behavior 7.3: It should default the date range to the last 30 days" (let [{:strs [test-client-id test-vendor-id test-account-id]} (setup-test-data [(test-client :db/id "test-client-id" :client/code "TEST01")]) now (time/now) days-ago-10 (time/minus now (time/days 10)) days-ago-40 (time/minus now (time/days 40))] @(dc/transact conn [{:db/id "invoice-1" :invoice/client test-client-id :invoice/vendor test-vendor-id :invoice/date (clj-time.coerce/to-date days-ago-10) :invoice/status :invoice-status/unpaid :invoice/import-status :import-status/imported :invoice/total 100.0 :invoice/outstanding-balance 100.0 :invoice/invoice-number "INV-001" :invoice/expense-accounts [{:invoice-expense-account/account test-account-id :invoice-expense-account/amount 100.0 :invoice-expense-account/location "DT"}]} {:db/id "invoice-2" :invoice/client test-client-id :invoice/vendor test-vendor-id :invoice/date (clj-time.coerce/to-date days-ago-40) :invoice/status :invoice-status/unpaid :invoice/import-status :import-status/imported :invoice/total 200.0 :invoice/outstanding-balance 200.0 :invoice/invoice-number "INV-002" :invoice/expense-accounts [{:invoice-expense-account/account test-account-id :invoice-expense-account/amount 200.0 :invoice-expense-account/location "DT"}]}]) ;; Default lookup should only include invoice from 10 days ago (let [data (expense-reports/lookup-invoice-total-data {:clients [{:db/id test-client-id}] :client {:db/id test-client-id} :query-params {}})] ;; Should include only invoice-1 (10 days ago, within 30 days) (is (>= 1 (count data))))))) (deftest test-push-filter-changes-to-history (testing "Behavior 7.6: It should push filter changes to browser history" (let [{:strs [test-client-id]} (setup-test-data [(test-client :db/id "test-client-id" :client/code "TEST01")])] ;; Test expense breakdown card pushes URL (let [response (expense-reports/expense-breakdown-card {:identity (user-token test-client-id) :clients [{:db/id test-client-id}] :client {:db/id test-client-id} :query-params {}})] (is (= 200 (:status response))) ;; Should have hx-push-url header (is (some? (get-in response [:headers "hx-push-url"])))) ;; Test invoice total card pushes URL (let [response (expense-reports/invoice-total-card {:identity (user-token test-client-id) :clients [{:db/id test-client-id}] :client {:db/id test-client-id} :query-params {}})] (is (= 200 (:status response))) ;; Should have hx-push-url header (is (some? (get-in response [:headers "hx-push-url"])))))))