Wire manual buy/sell to the mammon acquire/consume endpoints #14

Closed
opened 2026-07-06 20:56:21 +02:00 by Sirttas · 0 comments
Owner

Wire manual buy/sell to the mammon acquire/consume endpoints

Summary

The mammon backend exposes manual write endpoints (mammon #2): POST /activity/acquire and POST /activity/consume. The acquisition store still has stubbed no-ops from when no write endpoint existed — this issue wires them up, binds every acquisition/consumption to a ledger, adds an Eve-time field to the modals, and removes the now-redundant Scan buy button.

  • src/market/acquisition/acquisition.ts"the backend exposes no write endpoint yet, so buy/sell are no-ops."
    • addAcquiredType(type, quantity, price, source?) → currently {}
    • removeAcquiredType(id, quantity) → currently {}

The buy/sell UI already exists (BuyModal.vue, SellModal.vue, opened from TypeInfo.vue and AcquisitionsPanel.vue) and already calls these two store actions — so this is wiring + a ledger binding + a date input, not re-adding buttons.

Prerequisites

  1. Regenerate the spec + clientdone. docs/mammon-api.yml and src/generated/mammon already contain acquire/consume and the ManualActivityRequest/ActivitySourceRequest schemas, so activityApi.acquire(...) / activityApi.consume(...) exist. src/generated/mammon is generated — do not hand-edit.

Source model — always LEDGER

The old AcquiredTypeSource ('bo' | 'so' | 'prod' | 'misc') was vestigial — defined and only referenced as an unused param. Removed.

Every manual acquisition/consumption is bound to a ledger: the request source is always ActivitySourceRequest = { type: 'LEDGER', characterId: null, ledgerId }. NONE/CHARACTER are not used here; CORPORATION is rejected by the backend anyway.

How the ledger is determined depends on context:

  • Per-ledger tab (ViewLedgerAcquisitions.vue) — the ledger is fixed to that tab (ledgerId from useLedgerParam()). No select is shown; the modal binds to the tab's ledger directly (passed as the AcquisitionsPanel ledgerId prop → modal open()).
  • TypeInfo.vue and the account-wide Acquisitions.vue panel — no context ledger, so the modal shows a LedgerSelect (@/ledger, v-model-bound to a Ledger, backed by useLedgersStore().ledgers) for the user to pick. The submit button (Add/Remove) is disabled until a ledger is selected.

AcquisitionsPanel gains a ledgerId? prop it forwards to the modals: when set (per-ledger tab) the modal hides the select and uses it; when unset (account-wide) the modal shows the select.

Remove the Scan buy button

The Scan-page buy path is no longer required — removed:

  • src/market/scan/ScanResultTable.vue — dropped the buttons column (the ShoppingCartIcon cell), the buy emit, the Emits interface, the ShoppingCartIcon import, and the now-dead infoOnlybuttons handling in columnsToIgnore. (TypeInfo.vue keeps its own buy button.)
  • src/pages/market/Scan.vue — removed the BuyModal import/ref/element and the @buy handler.

Eve-time date field

Both modals gain a datetime-local input labelled "Eve Time" so the user can set when the acquisition/consumption happened, sent as the request's datetime.

  • EVE time is UTC/GMT. datetime-local is timezone-naive, so it is seeded with UTC-now and its value is read back as UTC — what is shown and stored is Eve time regardless of the browser's zone. Helpers live in AcquiredType.ts:
    • toEveDatetimeInput(date)date.toISOString().slice(0, 16) (seed value)
    • fromEveDatetimeInput(value)new Date(\${value}Z`) — returns a **Date`**
  • Both modals default the field to now on open().
  • The store actions take a Date (not a pre-formatted string) and call .toISOString() at the request boundary.

Change

  • acquisition.ts

    • Remove AcquiredTypeSource.
    • addAcquiredType(type, quantity, price, ledgerId, datetime: Date)activityApi.acquire({ source: { type: 'LEDGER', characterId: null, ledgerId }, marketTypeId: type, quantity, unitPrice: price, datetime: datetime.toISOString(), taxes: null, description: null }).
    • removeAcquiredType(id, quantity, ledgerId, datetime: Date) → look up the acquisition by id locally to get its marketTypeId, then activityApi.consume({ source: { type: 'LEDGER', characterId: null, ledgerId }, marketTypeId, quantity, unitPrice: 0, datetime: datetime.toISOString(), taxes: null, description: null }). The backend consume records a consumption of a market type (FIFO pools handle the draw-down); it takes no acquisition id. unitPrice is 0 because the sell captures no price and the generated ManualActivityRequest requires the field.
    • Adds processNewActivities() delegating to activityApi.processNewActivities().
  • Modals

    • BuyModal / SellModal own the Eve-time input, and the LedgerSelect only when no context ledger is supplied; open(...) receives the (optional) context ledgerId and forwards the resolved ledgerId + Date to the store action.
    • TypeInfo.vue opens BuyModal with no context ledger (select shown); AcquisitionsPanel opens both modals with its ledgerId prop — set → hidden/bound, unset → select shown.
    • Sell loop: SellModal iterates the acquisitions of the (single) market type, capping each consume at min(remaining-to-sell, pool.remaining). Since consume is by marketTypeId, the per-iteration quantities sum to the requested count (equivalent to a single consume of the total). One processNewActivities() after the batch, not per iteration.

Request body (ManualActivityRequest)

source (always { type: 'LEDGER', characterId: null, ledgerId }), marketTypeId, quantity, unitPrice, datetime (user-set, Eve/UTC), taxes: null, description: null.

Processing (make it show up in the list)

A created activity only appears in GET /acquisitions once processed. After the create call(s) the modal calls activityApi.processNewActivities() (via the store) — one call for the batched sell loop. If mammon #35 (immediate processing on acquire/consume) ships, this explicit call can be dropped.

Frontend auto-refresh was descoped. The originally-planned changed-event / both-context live re-fetch is not implemented — the account-wide page and per-ledger tab already have manual/auto Refresh, so live re-render after a write was left out of scope. AcquisitionsPanel does not emit changed; parents do not re-fetch on write.

Acceptance criteria

  • docs/mammon-api.yml + src/generated/mammon contain acquire/consume (already regenerated externally); activityApi.acquire / activityApi.consume available.
  • AcquiredTypeSource removed; no remaining references.
  • addAcquiredType records an acquisition via POST /activity/acquire bound to the resolved ledger.
  • removeAcquiredType records a consumption via POST /activity/consume bound to the resolved ledger (consume by marketTypeId, unitPrice: 0).
  • Per-ledger tab: no ledger select — bound to the tab's ledger. TypeInfo / account-wide: LedgerSelect shown, submit disabled until a ledger is chosen. Source is always LEDGER(ledgerId).
  • Both modals expose an "Eve Time" datetime-local input (default Eve/UTC now) whose value is sent as datetime; store actions take a Date.
  • The Scan buy button is removed (ScanResultTable buttons column + buy emit; Scan.vue modal wiring).
  • Processing triggered via processNewActivities() (one process for the batched sell loop) so entries surface in GET /acquisitions.
  • The no write endpoint yet comment is removed.
  • Refresh updates both the account-wide page and the per-ledger tabdescoped; relies on the existing manual/auto Refresh controls.

Notes / follow-ups

  • The generated src/generated/mammon/api.ts uses recursive extends interfaces (LedgerResponse, IskTransferResponse, ItemTransferResponse, MainLedgerResponse, …) that vue-tsc rejects — npm run build fails at baseline with ~32 pre-existing errors unrelated to this change. Worth fixing in the mammon-side codegen.

References

  • mammon #2 — manual acquire/consume endpoints (shipped, create-only).
  • mammon #35 — process activities immediately on manual acquire/consume (would remove the explicit processNewActivities call).
  • src/market/acquisition/{acquisition.ts,AcquiredType.ts,BuyModal.vue,SellModal.vue,AcquisitionsPanel.vue} — store, helpers, modals + panel.
  • src/ledger/LedgerSelect.vue, useLedgersStore / useLedgerParam (src/ledger/ledger.ts) — ledger select + context.
  • src/market/scan/ScanResultTable.vue, src/pages/market/Scan.vue — Scan buy button removed.
  • src/pages/market/TypeInfo.vue, src/pages/market/Acquisitions.vue, src/pages/ledger/ViewLedgerAcquisitions.vue — open sites.
  • src/mammon/mammonService.tsactivityApi, acquisitionApi instances.
# Wire manual buy/sell to the mammon acquire/consume endpoints ## Summary The mammon backend exposes manual write endpoints (mammon #2): `POST /activity/acquire` and `POST /activity/consume`. The acquisition store still has stubbed no-ops from when no write endpoint existed — this issue wires them up, binds every acquisition/consumption to a ledger, adds an Eve-time field to the modals, and removes the now-redundant Scan buy button. - `src/market/acquisition/acquisition.ts` — *"the backend exposes no write endpoint yet, so buy/sell are no-ops."* - `addAcquiredType(type, quantity, price, source?)` → currently `{}` - `removeAcquiredType(id, quantity)` → currently `{}` The buy/sell UI **already exists** (`BuyModal.vue`, `SellModal.vue`, opened from `TypeInfo.vue` and `AcquisitionsPanel.vue`) and already calls these two store actions — so this is *wiring + a ledger binding + a date input*, not re-adding buttons. ## Prerequisites 1. **Regenerate the spec + client** — **done.** `docs/mammon-api.yml` and `src/generated/mammon` already contain `acquire`/`consume` and the `ManualActivityRequest`/`ActivitySourceRequest` schemas, so `activityApi.acquire(...)` / `activityApi.consume(...)` exist. `src/generated/mammon` is generated — do not hand-edit. ## Source model — always LEDGER The old `AcquiredTypeSource` (`'bo' | 'so' | 'prod' | 'misc'`) was **vestigial** — defined and only referenced as an unused param. **Removed.** Every manual acquisition/consumption is **bound to a ledger**: the request source is always `ActivitySourceRequest = { type: 'LEDGER', characterId: null, ledgerId }`. `NONE`/`CHARACTER` are not used here; `CORPORATION` is rejected by the backend anyway. How the ledger is determined depends on context: - **Per-ledger tab** (`ViewLedgerAcquisitions.vue`) — the ledger is fixed to that tab (`ledgerId` from `useLedgerParam()`). **No select is shown**; the modal binds to the tab's ledger directly (passed as the `AcquisitionsPanel` `ledgerId` prop → modal `open()`). - **`TypeInfo.vue`** and the **account-wide** `Acquisitions.vue` panel — no context ledger, so the modal shows a **`LedgerSelect`** (`@/ledger`, `v-model`-bound to a `Ledger`, backed by `useLedgersStore().ledgers`) for the user to pick. The submit button (Add/Remove) is **disabled until a ledger is selected**. `AcquisitionsPanel` gains a `ledgerId?` prop it forwards to the modals: when set (per-ledger tab) the modal hides the select and uses it; when unset (account-wide) the modal shows the select. ## Remove the Scan buy button The Scan-page buy path is no longer required — **removed**: - `src/market/scan/ScanResultTable.vue` — dropped the `buttons` column (the `ShoppingCartIcon` cell), the `buy` emit, the `Emits` interface, the `ShoppingCartIcon` import, and the now-dead `infoOnly`→`buttons` handling in `columnsToIgnore`. (`TypeInfo.vue` keeps its own buy button.) - `src/pages/market/Scan.vue` — removed the `BuyModal` import/ref/element and the `@buy` handler. ## Eve-time date field Both modals gain a `datetime-local` input labelled **"Eve Time"** so the user can set when the acquisition/consumption happened, sent as the request's `datetime`. - EVE time is UTC/GMT. `datetime-local` is timezone-naive, so it is **seeded with UTC-now** and its value is **read back as UTC** — what is shown and stored is Eve time regardless of the browser's zone. Helpers live in `AcquiredType.ts`: - `toEveDatetimeInput(date)` → `date.toISOString().slice(0, 16)` (seed value) - `fromEveDatetimeInput(value)` → `new Date(\`${value}Z\`)` — returns a **`Date`** - Both modals default the field to now on `open()`. - The store actions take a **`Date`** (not a pre-formatted string) and call `.toISOString()` at the request boundary. ## Change - **`acquisition.ts`** - Remove `AcquiredTypeSource`. - `addAcquiredType(type, quantity, price, ledgerId, datetime: Date)` → `activityApi.acquire({ source: { type: 'LEDGER', characterId: null, ledgerId }, marketTypeId: type, quantity, unitPrice: price, datetime: datetime.toISOString(), taxes: null, description: null })`. - `removeAcquiredType(id, quantity, ledgerId, datetime: Date)` → look up the acquisition by `id` locally to get its `marketTypeId`, then `activityApi.consume({ source: { type: 'LEDGER', characterId: null, ledgerId }, marketTypeId, quantity, unitPrice: 0, datetime: datetime.toISOString(), taxes: null, description: null })`. The backend consume records a consumption of a *market type* (FIFO pools handle the draw-down); it takes no acquisition id. `unitPrice` is `0` because the sell captures no price and the generated `ManualActivityRequest` requires the field. - Adds `processNewActivities()` delegating to `activityApi.processNewActivities()`. - **Modals** - `BuyModal` / `SellModal` own the Eve-time input, and the `LedgerSelect` **only when no context ledger is supplied**; `open(...)` receives the (optional) context `ledgerId` and forwards the resolved `ledgerId` + `Date` to the store action. - `TypeInfo.vue` opens `BuyModal` with no context ledger (select shown); `AcquisitionsPanel` opens both modals with its `ledgerId` prop — set → hidden/bound, unset → select shown. - **Sell loop:** `SellModal` iterates the acquisitions of the (single) market type, capping each `consume` at `min(remaining-to-sell, pool.remaining)`. Since consume is by `marketTypeId`, the per-iteration quantities sum to the requested count (equivalent to a single consume of the total). One `processNewActivities()` after the batch, not per iteration. ### Request body (`ManualActivityRequest`) `source` (always `{ type: 'LEDGER', characterId: null, ledgerId }`), `marketTypeId`, `quantity`, `unitPrice`, `datetime` (user-set, Eve/UTC), `taxes: null`, `description: null`. ### Processing (make it show up in the list) A created activity only appears in `GET /acquisitions` once processed. After the create call(s) the modal calls `activityApi.processNewActivities()` (via the store) — one call for the batched sell loop. If mammon #35 (immediate processing on acquire/consume) ships, this explicit call can be dropped. **Frontend auto-refresh was descoped.** The originally-planned `changed`-event / both-context live re-fetch is **not** implemented — the account-wide page and per-ledger tab already have manual/auto Refresh, so live re-render after a write was left out of scope. `AcquisitionsPanel` does **not** emit `changed`; parents do not re-fetch on write. ## Acceptance criteria - [x] `docs/mammon-api.yml` + `src/generated/mammon` contain `acquire`/`consume` (already regenerated externally); `activityApi.acquire` / `activityApi.consume` available. - [x] `AcquiredTypeSource` removed; no remaining references. - [x] `addAcquiredType` records an acquisition via `POST /activity/acquire` bound to the resolved ledger. - [x] `removeAcquiredType` records a consumption via `POST /activity/consume` bound to the resolved ledger (consume by `marketTypeId`, `unitPrice: 0`). - [x] Per-ledger tab: **no ledger select** — bound to the tab's ledger. TypeInfo / account-wide: `LedgerSelect` shown, submit disabled until a ledger is chosen. Source is always `LEDGER(ledgerId)`. - [x] Both modals expose an **"Eve Time"** `datetime-local` input (default Eve/UTC now) whose value is sent as `datetime`; store actions take a `Date`. - [x] The Scan buy button is removed (`ScanResultTable` buttons column + `buy` emit; `Scan.vue` modal wiring). - [x] Processing triggered via `processNewActivities()` (one process for the batched sell loop) so entries surface in `GET /acquisitions`. - [x] The `no write endpoint yet` comment is removed. - [x] ~~Refresh updates both the account-wide page and the per-ledger tab~~ — **descoped**; relies on the existing manual/auto Refresh controls. ## Notes / follow-ups - The generated `src/generated/mammon/api.ts` uses recursive `extends` interfaces (`LedgerResponse`, `IskTransferResponse`, `ItemTransferResponse`, `MainLedgerResponse`, …) that `vue-tsc` rejects — `npm run build` fails at baseline with ~32 pre-existing errors unrelated to this change. Worth fixing in the mammon-side codegen. ## References - mammon #2 — manual acquire/consume endpoints (shipped, create-only). - mammon #35 — process activities immediately on manual acquire/consume (would remove the explicit `processNewActivities` call). - `src/market/acquisition/{acquisition.ts,AcquiredType.ts,BuyModal.vue,SellModal.vue,AcquisitionsPanel.vue}` — store, helpers, modals + panel. - `src/ledger/LedgerSelect.vue`, `useLedgersStore` / `useLedgerParam` (`src/ledger/ledger.ts`) — ledger select + context. - `src/market/scan/ScanResultTable.vue`, `src/pages/market/Scan.vue` — Scan buy button removed. - `src/pages/market/TypeInfo.vue`, `src/pages/market/Acquisitions.vue`, `src/pages/ledger/ViewLedgerAcquisitions.vue` — open sites. - `src/mammon/mammonService.ts` — `activityApi`, `acquisitionApi` instances.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: eveal/gemory#14