Commit Graph
15 Commits
Author SHA1 Message Date
efrilmandClaude Opus 5.5 39e47ff0e6 feat(loyalty): typed loyalty settings with change history
Adds LoyaltySettingsProcessor (docs/prd-point-coin.md F1, F2, F12, PC-109).

Reading returns typed settings for an outlet (earning per currency, paying
with EnakPoint) and for an organization (point value, exchange rate,
transfers, and the expiry settings awaiting note N4). A key that was never
set takes the PRD default. A stored value that is unusable, such as an
earn_per_amount of 0 that would divide by zero, also falls back to the
default and is logged, so a bad row never reaches a calculation.

Writing takes the whole settings struct, validates every rule in the PRD
before touching the database, and stores and records in
loyalty_setting_changes only the keys whose effective value changes: old
value (NULL while it was on its default), new value, and who changed it.
Clearing a limit deletes the stored value. Each save runs in one
transaction under an advisory lock per outlet or organization, so two saves
at once cannot both compute their change from the same old value. The
outlet must belong to the caller's organization.

Every key is described once (key, default, valid range, bound field), and
reading, validating and diffing all use that description.

GET /customer/wallet now reads the point value through this processor; the
minimal organization settings repository from PC-106 is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 10:23:08 +07:00
efrilmandClaude Opus 5.5 fc5eecb68a feat(wallet): add wallet entities and repository
Entities for the four wallet tables and a WalletRepository that the wallet
processor will build on (PC-103).

Every method goes through the caller's transaction, and writes and locks
refuse to run without one: outside a transaction a lock is released as soon
as it is taken and a balance could move without its ledger row.

- LockWallet creates the wallet on first use, taking the organization from
  the customer, then locks it with SELECT ... FOR UPDATE.
- LockWallets always locks in customer_id order so opposite transfers
  cannot deadlock.
- AddBalance and ConsumeLot are conditional updates that return an error
  when they would overdraw, instead of tripping the CHECK constraint.
- ListActiveLots returns unexpired lots with balance in K9 spending order.

The tests need a real Postgres and run only when TEST_DATABASE_URL points at
a migrated database. Both the lock and the lock ordering were checked by
removing them and watching the tests fail (lost update, deadlock detected).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 01:01:03 +07:00
Efril 2c6864147b feat: cash advance 2026-08-13 14:38:28 +07:00
ryan da87d659df Add expense CRUD 2026-05-25 14:59:40 +07:00
ryan 4130cb66df refactor and add outlet product table 2026-05-13 21:58:54 +07:00
Efril ddaf6df436 migration notification 2026-05-10 12:35:44 +07:00
Efril bbd6666299 user devices 2026-05-10 10:42:09 +07:00
Aditya Siregar 65f61b65cf user auth register 2025-09-18 01:32:01 +07:00
Aditya Siregar 155016dec8 Add Campaign 2025-09-17 23:55:11 +07:00
Aditya Siregar 201e24041b Add Tiers and Game Prize 2025-09-17 19:30:17 +07:00
Aditya Siregar 12ee54390f Revert "voucher"
This reverts commit cfe690a40f.
2025-09-16 19:31:39 +07:00
Aditya Siregar cfe690a40f voucher 2025-09-13 15:37:26 +07:00
Aditya Siregar efe09c21e4 Add coa purchase and vendors 2025-09-12 01:12:11 +07:00
Aditya Siregar a759e0f57c init 2025-07-30 23:18:20 +07:00
aditya.siregar 4f5950543e init 2025-07-18 20:10:29 +07:00