New eveal #32
+128
-1
@@ -350,6 +350,46 @@ paths:
|
||||
description: New activities fetched and stored
|
||||
"400":
|
||||
description: No character with this id
|
||||
/activity/consume:
|
||||
post:
|
||||
tags:
|
||||
- activity
|
||||
summary: Manually record a consumption (sell) of an item. Call POST /activities/process
|
||||
to fold it into acquisitions.
|
||||
operationId: consume
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ManualActivityRequest"
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: Consumption recorded
|
||||
"400":
|
||||
description: Invalid request (e.g. CORPORATION source)
|
||||
"404":
|
||||
description: Source not owned by the caller
|
||||
/activity/acquire:
|
||||
post:
|
||||
tags:
|
||||
- activity
|
||||
summary: Manually record an acquisition (buy) of an item. Call POST /activities/process
|
||||
to fold it into acquisitions.
|
||||
operationId: acquire
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ManualActivityRequest"
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: Acquisition recorded
|
||||
"400":
|
||||
description: Invalid request (e.g. CORPORATION source)
|
||||
"404":
|
||||
description: Source not owned by the caller
|
||||
/activities/process:
|
||||
post:
|
||||
tags:
|
||||
@@ -1123,6 +1163,84 @@ components:
|
||||
required:
|
||||
- memberLedgerIds
|
||||
- name
|
||||
ActivitySourceRequest:
|
||||
type: object
|
||||
description: "Where a manual activity is attributed: a character, a ledger,\
|
||||
\ or nothing. CORPORATION is not accepted."
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
description: The kind of source.
|
||||
enum:
|
||||
- CHARACTER
|
||||
- LEDGER
|
||||
- NONE
|
||||
example: CHARACTER
|
||||
characterId:
|
||||
type:
|
||||
- integer
|
||||
- "null"
|
||||
format: int64
|
||||
description: "EVE character id, when the source is a character."
|
||||
example: 2112625428
|
||||
ledgerId:
|
||||
type:
|
||||
- string
|
||||
- "null"
|
||||
format: uuid
|
||||
description: "Ledger id, when the source is a ledger."
|
||||
example: 0199a1b2-c3d4-7e5f-8a90-1b2c3d4e5f60
|
||||
required:
|
||||
- characterId
|
||||
- ledgerId
|
||||
- type
|
||||
ManualActivityRequest:
|
||||
type: object
|
||||
description: Request to manually record an acquisition (buy) or consumption
|
||||
(sell) of an item.
|
||||
properties:
|
||||
source:
|
||||
$ref: "#/components/schemas/ActivitySourceRequest"
|
||||
description: Where the activity is attributed.
|
||||
marketTypeId:
|
||||
type: integer
|
||||
format: int64
|
||||
description: EVE type id of the traded item.
|
||||
example: 34
|
||||
quantity:
|
||||
type: integer
|
||||
format: int64
|
||||
description: Number of units.
|
||||
example: 1000
|
||||
unitPrice:
|
||||
type: number
|
||||
description: Price per unit.
|
||||
example: 5.42
|
||||
datetime:
|
||||
type:
|
||||
- string
|
||||
- "null"
|
||||
format: date-time
|
||||
description: When the activity happened. Defaults to now when omitted.
|
||||
taxes:
|
||||
type:
|
||||
- number
|
||||
- "null"
|
||||
description: Taxes paid. Defaults to 0 when omitted.
|
||||
example: 0
|
||||
description:
|
||||
type:
|
||||
- string
|
||||
- "null"
|
||||
description: Free-form description.
|
||||
required:
|
||||
- datetime
|
||||
- description
|
||||
- marketTypeId
|
||||
- quantity
|
||||
- source
|
||||
- taxes
|
||||
- unitPrice
|
||||
CharacterResponse:
|
||||
type: object
|
||||
description: An EVE Online character.
|
||||
@@ -1507,7 +1625,7 @@ components:
|
||||
ActivitySourceResponse:
|
||||
type: object
|
||||
description: "Where an activity or transaction came from: a character, a corporation\
|
||||
\ wallet, or nothing (a manual entry)."
|
||||
\ wallet, a ledger, or nothing (a manual entry)."
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
@@ -1515,6 +1633,7 @@ components:
|
||||
enum:
|
||||
- CHARACTER
|
||||
- CORPORATION
|
||||
- LEDGER
|
||||
- NONE
|
||||
example: CHARACTER
|
||||
characterId:
|
||||
@@ -1539,10 +1658,18 @@ components:
|
||||
description: "Corporation wallet division (1-7), when the source is a corporation\
|
||||
\ and it is known."
|
||||
example: 4
|
||||
ledgerId:
|
||||
type:
|
||||
- string
|
||||
- "null"
|
||||
format: uuid
|
||||
description: "Ledger id, when the source is a ledger."
|
||||
example: 0199a1b2-c3d4-7e5f-8a90-1b2c3d4e5f60
|
||||
required:
|
||||
- characterId
|
||||
- corporationId
|
||||
- division
|
||||
- ledgerId
|
||||
- type
|
||||
IskTransferResponse:
|
||||
allOf:
|
||||
|
||||
+201
-1
@@ -61,7 +61,33 @@ export interface AcquisitionResponse {
|
||||
'unitCost': number;
|
||||
}
|
||||
/**
|
||||
* Where an activity or transaction came from: a character, a corporation wallet, or nothing (a manual entry).
|
||||
* Where a manual activity is attributed: a character, a ledger, or nothing. CORPORATION is not accepted.
|
||||
*/
|
||||
export interface ActivitySourceRequest {
|
||||
/**
|
||||
* The kind of source.
|
||||
*/
|
||||
'type': ActivitySourceRequestTypeEnum;
|
||||
/**
|
||||
* EVE character id, when the source is a character.
|
||||
*/
|
||||
'characterId': number | null;
|
||||
/**
|
||||
* Ledger id, when the source is a ledger.
|
||||
*/
|
||||
'ledgerId': string | null;
|
||||
}
|
||||
|
||||
export const ActivitySourceRequestTypeEnum = {
|
||||
Character: 'CHARACTER',
|
||||
Ledger: 'LEDGER',
|
||||
None: 'NONE',
|
||||
} as const;
|
||||
|
||||
export type ActivitySourceRequestTypeEnum = typeof ActivitySourceRequestTypeEnum[keyof typeof ActivitySourceRequestTypeEnum];
|
||||
|
||||
/**
|
||||
* Where an activity or transaction came from: a character, a corporation wallet, a ledger, or nothing (a manual entry).
|
||||
*/
|
||||
export interface ActivitySourceResponse {
|
||||
/**
|
||||
@@ -80,11 +106,16 @@ export interface ActivitySourceResponse {
|
||||
* Corporation wallet division (1-7), when the source is a corporation and it is known.
|
||||
*/
|
||||
'division': number | null;
|
||||
/**
|
||||
* Ledger id, when the source is a ledger.
|
||||
*/
|
||||
'ledgerId': string | null;
|
||||
}
|
||||
|
||||
export const ActivitySourceResponseTypeEnum = {
|
||||
Character: 'CHARACTER',
|
||||
Corporation: 'CORPORATION',
|
||||
Ledger: 'LEDGER',
|
||||
None: 'NONE',
|
||||
} as const;
|
||||
|
||||
@@ -271,6 +302,39 @@ export interface MainLedgerResponse extends LedgerResponse {
|
||||
*/
|
||||
'balance': number;
|
||||
}
|
||||
/**
|
||||
* Request to manually record an acquisition (buy) or consumption (sell) of an item.
|
||||
*/
|
||||
export interface ManualActivityRequest {
|
||||
/**
|
||||
* Where the activity is attributed.
|
||||
*/
|
||||
'source': ActivitySourceRequest;
|
||||
/**
|
||||
* EVE type id of the traded item.
|
||||
*/
|
||||
'marketTypeId': number;
|
||||
/**
|
||||
* Number of units.
|
||||
*/
|
||||
'quantity': number;
|
||||
/**
|
||||
* Price per unit.
|
||||
*/
|
||||
'unitPrice': number;
|
||||
/**
|
||||
* When the activity happened. Defaults to now when omitted.
|
||||
*/
|
||||
'datetime': string | null;
|
||||
/**
|
||||
* Taxes paid. Defaults to 0 when omitted.
|
||||
*/
|
||||
'taxes': number | null;
|
||||
/**
|
||||
* Free-form description.
|
||||
*/
|
||||
'description': string | null;
|
||||
}
|
||||
/**
|
||||
* A single day of market history for a market type.
|
||||
*/
|
||||
@@ -803,6 +867,74 @@ export class AcquisitionApi extends BaseAPI {
|
||||
*/
|
||||
export const ActivityApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @summary Manually record an acquisition (buy) of an item. Call POST /activities/process to fold it into acquisitions.
|
||||
* @param {ManualActivityRequest} manualActivityRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
acquire: async (manualActivityRequest: ManualActivityRequest, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'manualActivityRequest' is not null or undefined
|
||||
assertParamExists('acquire', 'manualActivityRequest', manualActivityRequest)
|
||||
const localVarPath = `/activity/acquire`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(manualActivityRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Manually record a consumption (sell) of an item. Call POST /activities/process to fold it into acquisitions.
|
||||
* @param {ManualActivityRequest} manualActivityRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
consume: async (manualActivityRequest: ManualActivityRequest, options: RawAxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'manualActivityRequest' is not null or undefined
|
||||
assertParamExists('consume', 'manualActivityRequest', manualActivityRequest)
|
||||
const localVarPath = `/activity/consume`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(manualActivityRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Fetch new activities for the authenticated user\'s characters from the EVE API
|
||||
@@ -903,6 +1035,32 @@ export const ActivityApiAxiosParamCreator = function (configuration?: Configurat
|
||||
export const ActivityApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = ActivityApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @summary Manually record an acquisition (buy) of an item. Call POST /activities/process to fold it into acquisitions.
|
||||
* @param {ManualActivityRequest} manualActivityRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async acquire(manualActivityRequest: ManualActivityRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.acquire(manualActivityRequest, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['ActivityApi.acquire']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Manually record a consumption (sell) of an item. Call POST /activities/process to fold it into acquisitions.
|
||||
* @param {ManualActivityRequest} manualActivityRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async consume(manualActivityRequest: ManualActivityRequest, options?: RawAxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.consume(manualActivityRequest, options);
|
||||
const localVarOperationServerIndex = configuration?.serverIndex ?? 0;
|
||||
const localVarOperationServerBasePath = operationServerMap['ActivityApi.consume']?.[localVarOperationServerIndex]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, localVarOperationServerBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Fetch new activities for the authenticated user\'s characters from the EVE API
|
||||
@@ -949,6 +1107,26 @@ export const ActivityApiFp = function(configuration?: Configuration) {
|
||||
export const ActivityApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = ActivityApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @summary Manually record an acquisition (buy) of an item. Call POST /activities/process to fold it into acquisitions.
|
||||
* @param {ManualActivityRequest} manualActivityRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
acquire(manualActivityRequest: ManualActivityRequest, options?: RawAxiosRequestConfig): AxiosPromise<void> {
|
||||
return localVarFp.acquire(manualActivityRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Manually record a consumption (sell) of an item. Call POST /activities/process to fold it into acquisitions.
|
||||
* @param {ManualActivityRequest} manualActivityRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
consume(manualActivityRequest: ManualActivityRequest, options?: RawAxiosRequestConfig): AxiosPromise<void> {
|
||||
return localVarFp.consume(manualActivityRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary Fetch new activities for the authenticated user\'s characters from the EVE API
|
||||
@@ -984,6 +1162,28 @@ export const ActivityApiFactory = function (configuration?: Configuration, baseP
|
||||
* ActivityApi - object-oriented interface
|
||||
*/
|
||||
export class ActivityApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @summary Manually record an acquisition (buy) of an item. Call POST /activities/process to fold it into acquisitions.
|
||||
* @param {ManualActivityRequest} manualActivityRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public acquire(manualActivityRequest: ManualActivityRequest, options?: RawAxiosRequestConfig) {
|
||||
return ActivityApiFp(this.configuration).acquire(manualActivityRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @summary Manually record a consumption (sell) of an item. Call POST /activities/process to fold it into acquisitions.
|
||||
* @param {ManualActivityRequest} manualActivityRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
public consume(manualActivityRequest: ManualActivityRequest, options?: RawAxiosRequestConfig) {
|
||||
return ActivityApiFp(this.configuration).consume(manualActivityRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @summary Fetch new activities for the authenticated user\'s characters from the EVE API
|
||||
|
||||
@@ -8,3 +8,6 @@ export type AcquiredType = Omit<RawAcquiredType, 'type'> & {
|
||||
}
|
||||
|
||||
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,10 +14,18 @@ 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 = {};
|
||||
@@ -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) {
|
||||
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 };
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@ import {SliderCheckbox} from '@/components';
|
||||
import {SortableHeader, useSort, VirtualScrollTable} from '@/components/table';
|
||||
import {formatIsk, percentFormater} from "@/formaters";
|
||||
import {MarketTrend, MarketTrendIcon, MarketTrends, MarketType, MarketTypeLabel, TrendFilter} from "@/market";
|
||||
import {ShoppingCartIcon} from '@heroicons/vue/24/outline';
|
||||
import {useStorage} from '@vueuse/core';
|
||||
import {computed, ref} from 'vue';
|
||||
import {useAcquiredTypesStore} from '../acquisition';
|
||||
@@ -31,10 +30,6 @@ interface Props {
|
||||
ignoredColums?: string[] | string;
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'buy', type: MarketType, buy: number, sell: number): void;
|
||||
}
|
||||
|
||||
const scoreFormater = new Intl.NumberFormat("en-US", {
|
||||
maximumFractionDigits: 0
|
||||
});
|
||||
@@ -47,7 +42,6 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
infoOnly: false,
|
||||
ignoredColums: () => []
|
||||
});
|
||||
defineEmits<Emits>();
|
||||
|
||||
const acquiredTypesStore = useAcquiredTypesStore();
|
||||
|
||||
@@ -59,14 +53,9 @@ const trackedTrends = useStorage<MarketTrend[]>('market-scan-trends', [
|
||||
MarketTrends.Flat,
|
||||
MarketTrends.Down,
|
||||
]);
|
||||
const columnsToIgnore = computed(() => {
|
||||
const ic = typeof props.ignoredColums === 'string' ? [props.ignoredColums] : props.ignoredColums;
|
||||
|
||||
if (props.infoOnly && !ic.includes('buttons')) {
|
||||
return [...ic, 'buttons'];
|
||||
}
|
||||
return ic;
|
||||
});
|
||||
const columnsToIgnore = computed(() =>
|
||||
typeof props.ignoredColums === 'string' ? [props.ignoredColums] : props.ignoredColums
|
||||
);
|
||||
const { sortedArray, headerProps, showColumn } = useSort<Result>(computed(() => props.items
|
||||
.filter(r => r.type.name.toLowerCase().includes(filter.value.toLowerCase()))
|
||||
.filter(r => trackedTrends.value.includes(r.trend))
|
||||
@@ -142,7 +131,6 @@ const getLineColor = (result: Result) => {
|
||||
<SortableHeader v-bind="headerProps" sortKey="profit">Profit</SortableHeader>
|
||||
<SortableHeader v-bind="headerProps" sortKey="score">Score</SortableHeader>
|
||||
<SortableHeader v-bind="headerProps" sortKey="acquisitions">Acquisitions</SortableHeader>
|
||||
<SortableHeader v-bind="headerProps" sortKey="buttons" unsortable />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -162,9 +150,6 @@ const getLineColor = (result: Result) => {
|
||||
<td v-if="showColumn('profit')" class="text-right">{{ percentFormater.format(r.data.profit) }}</td>
|
||||
<td v-if="showColumn('score')" class="text-right">{{ scoreFormater.format(r.data.score) }}</td>
|
||||
<td v-if="showColumn('acquisitions')" class="text-right">{{ r.data.acquisitions }}</td>
|
||||
<td v-if="showColumn('buttons')" class="text-right">
|
||||
<button class="btn-icon me-1" title="Add acquisitions" @click="$emit('buy', r.data.type, r.data.buy, r.data.sell)"><ShoppingCartIcon /></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</template>
|
||||
|
||||
@@ -19,6 +19,6 @@ const items = computedAsync(async () => {
|
||||
|
||||
<template>
|
||||
<div class="mt-4">
|
||||
<AcquisitionsPanel :items="items" :ignoredColums="['date', 'ledger']" />
|
||||
<AcquisitionsPanel :items="items" :ledgerId="ledgerId" :ignoredColums="['date', 'ledger']" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,13 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import {getMarketTypes, TaxInput, useMarketTaxStore} from "@/market";
|
||||
import {BuyModal} from '@/market/acquisition';
|
||||
import {ScanResult, ScanResultTable, toScanResult} from '@/market/scan';
|
||||
import {marketApi} from "@/mammon";
|
||||
import {useStorage} from "@vueuse/core";
|
||||
import {ref, watch} from 'vue';
|
||||
|
||||
const buyModal = ref<typeof BuyModal>();
|
||||
|
||||
const marketTaxStore = useMarketTaxStore();
|
||||
const days = useStorage('market-scan-days', 365);
|
||||
const items = ref<ScanResult[]>([]);
|
||||
@@ -51,8 +48,7 @@ watch([days, () => marketTaxStore.brokerFee, () => marketTaxStore.scc], scan, {
|
||||
<span>Scanning market…</span>
|
||||
</div>
|
||||
<template v-else>
|
||||
<ScanResultTable :items="items" @buy="(type, buy, sell) => buyModal?.open(type, { 'Buy': buy, 'Sell': sell })" />
|
||||
<BuyModal ref="buyModal" />
|
||||
<ScanResultTable :items="items" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user