feat(#14): Wire manual buy/sell to the mammon acquire/consume endpoints
This commit is contained in:
@@ -7,4 +7,7 @@ export type AcquiredType = Omit<RawAcquiredType, 'type'> & {
|
||||
sell: number
|
||||
}
|
||||
|
||||
export const acquiredTypesToSorted = <T extends {date: Date} = AcquiredType>(array: T[], reverse?: boolean) => array.toSorted((a, b) => reverse ? b.date.getTime() - a.date.getTime() : a.date.getTime() - b.date.getTime())
|
||||
export const acquiredTypesToSorted = <T extends {date: Date} = AcquiredType>(array: T[], reverse?: boolean) => array.toSorted((a, b) => reverse ? b.date.getTime() - a.date.getTime() : a.date.getTime() - b.date.getTime())
|
||||
|
||||
export const toEveDatetimeInput = (date: Date) => date.toISOString().slice(0, 16)
|
||||
export const fromEveDatetimeInput = (value: string) => new Date(`${value}Z`)
|
||||
@@ -10,6 +10,7 @@ import SellModal from './SellModal.vue';
|
||||
interface Props {
|
||||
items: RawAcquiredType[];
|
||||
ignoredColums?: string[] | string;
|
||||
ledgerId?: string;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
@@ -44,7 +45,7 @@ watch(() => props.items, async itms => {
|
||||
|
||||
<template>
|
||||
<template v-if="enriched.length > 0">
|
||||
<AcquisitionResultTable :items="enriched" :ignoredColums="ignoredColums" @buy="(types, price, buy, sell) => buyModal?.open(types[0].type, { 'Price': price, 'Buy': buy, 'Sell': sell })" @sell="types => sellModal?.open(types)" />
|
||||
<AcquisitionResultTable :items="enriched" :ignoredColums="ignoredColums" @buy="(types, price, buy, sell) => buyModal?.open(types[0].type, { 'Price': price, 'Buy': buy, 'Sell': sell }, props.ledgerId)" @sell="types => sellModal?.open(types, props.ledgerId)" />
|
||||
<BuyModal ref="buyModal" />
|
||||
<SellModal ref="sellModal" />
|
||||
</template>
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
import {Modal} from '@/components';
|
||||
import {formatIsk} from '@/formaters';
|
||||
import {MarketType, MarketTypeLabel} from '@/market';
|
||||
import {ref} from 'vue';
|
||||
import {Ledger, LedgerSelect} from '@/ledger';
|
||||
import {computed, ref} from 'vue';
|
||||
import {fromEveDatetimeInput, toEveDatetimeInput} from './AcquiredType';
|
||||
import {useAcquiredTypesStore} from './acquisition';
|
||||
|
||||
const acquiredTypesStore = useAcquiredTypesStore();
|
||||
@@ -12,11 +14,19 @@ const type = ref<MarketType>();
|
||||
const suggestions = ref<Record<string, number>>({});
|
||||
const price = ref(1000000);
|
||||
const count = ref(1);
|
||||
const date = ref('');
|
||||
const contextLedgerId = ref<string>();
|
||||
const ledger = ref<Ledger>();
|
||||
|
||||
const open = (t: MarketType, s?: Record<string, number> | number) => {
|
||||
const ledgerId = computed(() => contextLedgerId.value ?? ledger.value?.ledgerId);
|
||||
|
||||
const open = (t: MarketType, s?: Record<string, number> | number, ledgerId?: string) => {
|
||||
type.value = t;
|
||||
count.value = 1;
|
||||
|
||||
date.value = toEveDatetimeInput(new Date());
|
||||
contextLedgerId.value = ledgerId;
|
||||
ledger.value = undefined;
|
||||
|
||||
if (typeof s === 'number') {
|
||||
suggestions.value = {};
|
||||
price.value = s;
|
||||
@@ -29,7 +39,7 @@ const open = (t: MarketType, s?: Record<string, number> | number) => {
|
||||
}
|
||||
modalOpen.value = true;
|
||||
}
|
||||
const add = () => {
|
||||
const add = async () => {
|
||||
const id = type.value?.id;
|
||||
|
||||
if (!id) {
|
||||
@@ -37,7 +47,14 @@ const add = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
acquiredTypesStore.addAcquiredType(id, count.value, price.value);
|
||||
const lid = ledgerId.value;
|
||||
|
||||
if (!lid) {
|
||||
return;
|
||||
}
|
||||
|
||||
await acquiredTypesStore.addAcquiredType(id, count.value, price.value, lid, fromEveDatetimeInput(date.value));
|
||||
await acquiredTypesStore.processNewActivities();
|
||||
modalOpen.value = false;
|
||||
}
|
||||
|
||||
@@ -61,7 +78,15 @@ defineExpose({ open });
|
||||
<span>Count: </span>
|
||||
<input class="ms-2" type="number" min="0" step="1" v-model="count" @keyup.enter="add" />
|
||||
</div>
|
||||
<button class="mb-auto" @click="add">Add</button>
|
||||
<div class="flex me-2 mb-auto">
|
||||
<span>Eve Time: </span>
|
||||
<input class="ms-2" type="datetime-local" v-model="date" />
|
||||
</div>
|
||||
<div v-if="!contextLedgerId" class="flex me-2 mb-auto">
|
||||
<span>Ledger: </span>
|
||||
<LedgerSelect class="ms-2" v-model="ledger" />
|
||||
</div>
|
||||
<button class="mb-auto" :disabled="!ledgerId" @click="add">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { Modal } from '@/components';
|
||||
import { MarketType, MarketTypeLabel } from '@/market';
|
||||
import { ref } from 'vue';
|
||||
import { AcquiredType, acquiredTypesToSorted } from './AcquiredType';
|
||||
import { Ledger, LedgerSelect } from '@/ledger';
|
||||
import { computed, ref } from 'vue';
|
||||
import { AcquiredType, acquiredTypesToSorted, fromEveDatetimeInput, toEveDatetimeInput } from './AcquiredType';
|
||||
import { useAcquiredTypesStore } from './acquisition';
|
||||
|
||||
|
||||
@@ -11,9 +12,14 @@ const acquiredTypesStore = useAcquiredTypesStore();
|
||||
const modalOpen = ref<boolean>(false);
|
||||
const type = ref<MarketType>();
|
||||
const count = ref(1);
|
||||
const date = ref('');
|
||||
const types = ref<AcquiredType[]>([]);
|
||||
const contextLedgerId = ref<string>();
|
||||
const ledger = ref<Ledger>();
|
||||
|
||||
const open = (t: AcquiredType[]) => {
|
||||
const ledgerId = computed(() => contextLedgerId.value ?? ledger.value?.ledgerId);
|
||||
|
||||
const open = (t: AcquiredType[], ledgerId?: string) => {
|
||||
if (t.length === 0) {
|
||||
return;
|
||||
}
|
||||
@@ -21,25 +27,37 @@ const open = (t: AcquiredType[]) => {
|
||||
types.value = acquiredTypesToSorted(t);
|
||||
type.value = t[0].type;
|
||||
count.value = 1;
|
||||
date.value = toEveDatetimeInput(new Date());
|
||||
contextLedgerId.value = ledgerId;
|
||||
ledger.value = undefined;
|
||||
modalOpen.value = true;
|
||||
}
|
||||
const remove = async () => {
|
||||
if (!types.value) {
|
||||
const remove = async () => {
|
||||
if (types.value.length === 0) {
|
||||
modalOpen.value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let c = count.value;
|
||||
const lid = ledgerId.value;
|
||||
|
||||
for (const type of types.value) {
|
||||
const remaining = type.remaining;
|
||||
if (!lid) {
|
||||
return;
|
||||
}
|
||||
|
||||
await acquiredTypesStore.removeAcquiredType(type.id, c);
|
||||
c -= remaining;
|
||||
if (c <= 0) {
|
||||
const datetime = fromEveDatetimeInput(date.value);
|
||||
let remaining = count.value;
|
||||
|
||||
for (const t of types.value) {
|
||||
if (remaining <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const quantity = Math.min(remaining, t.remaining);
|
||||
|
||||
await acquiredTypesStore.removeAcquiredType(t.id, quantity, lid, datetime);
|
||||
remaining -= quantity;
|
||||
}
|
||||
await acquiredTypesStore.processNewActivities();
|
||||
modalOpen.value = false;
|
||||
}
|
||||
|
||||
@@ -61,7 +79,15 @@ defineExpose({ open });
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="mb-auto" @click="remove">Remove</button>
|
||||
<div class="flex me-2 mb-auto">
|
||||
<span>Eve Time: </span>
|
||||
<input class="ms-2" type="datetime-local" v-model="date" />
|
||||
</div>
|
||||
<div v-if="!contextLedgerId" class="flex me-2 mb-auto">
|
||||
<span>Ledger: </span>
|
||||
<LedgerSelect class="ms-2" v-model="ledger" />
|
||||
</div>
|
||||
<button class="mb-auto" :disabled="!ledgerId" @click="remove">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import {defineStore} from "pinia";
|
||||
import {computed, ref} from "vue";
|
||||
import {acquisitionApi} from "@/mammon";
|
||||
import {acquisitionApi, activityApi} from "@/mammon";
|
||||
import {AcquisitionResponse, ActivitySourceResponse} from "@/generated/mammon";
|
||||
|
||||
export type AcquiredTypeSource = 'bo' | 'so' | 'prod' | 'misc';
|
||||
|
||||
export type RawAcquiredType = {
|
||||
id: string;
|
||||
ledgerId: string;
|
||||
@@ -32,14 +30,41 @@ export const useAcquiredTypesStore = defineStore('market-acquisition', () => {
|
||||
|
||||
const types = computed(() => acquiredTypes.value.filter(item => item.remaining > 0));
|
||||
|
||||
// Display-only: the backend exposes no write endpoint yet, so buy/sell are no-ops.
|
||||
const addAcquiredType = async (_type: number, _quantity: number, _price: number, _source?: AcquiredTypeSource) => {};
|
||||
const removeAcquiredType = async (_id: string, _quantity: number) => {};
|
||||
const addAcquiredType = (type: number, quantity: number, price: number, ledgerId: string, datetime: Date) =>
|
||||
activityApi.acquire({
|
||||
source: { type: 'LEDGER', characterId: null, ledgerId },
|
||||
marketTypeId: type,
|
||||
quantity,
|
||||
unitPrice: price,
|
||||
datetime: datetime.toISOString(),
|
||||
taxes: null,
|
||||
description: null,
|
||||
});
|
||||
|
||||
const removeAcquiredType = (id: string, quantity: number, ledgerId: string, datetime: Date) => {
|
||||
const acquisition = acquiredTypes.value.find(a => a.id === id);
|
||||
|
||||
if (!acquisition) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return activityApi.consume({
|
||||
source: { type: 'LEDGER', characterId: null, ledgerId },
|
||||
marketTypeId: acquisition.type,
|
||||
quantity,
|
||||
unitPrice: 0,
|
||||
datetime: datetime.toISOString(),
|
||||
taxes: null,
|
||||
description: null,
|
||||
});
|
||||
};
|
||||
|
||||
const processNewActivities = () => activityApi.processNewActivities();
|
||||
|
||||
const refresh = () => acquisitionApi.findAllAcquisitions()
|
||||
.then(response => acquiredTypes.value = response.data.map(toAcquiredType));
|
||||
|
||||
refresh();
|
||||
|
||||
return { acquiredTypes: types, addAcquiredType, removeAcquiredType, refresh };
|
||||
return { acquiredTypes: types, addAcquiredType, removeAcquiredType, processNewActivities, refresh };
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user