diff --git a/apps/api/src/modules/pricing/routes/admin-pricing-routes.js b/apps/api/src/modules/pricing/routes/admin-pricing-routes.js index fb29e76..0d4f889 100644 --- a/apps/api/src/modules/pricing/routes/admin-pricing-routes.js +++ b/apps/api/src/modules/pricing/routes/admin-pricing-routes.js @@ -36,6 +36,7 @@ export async function registerAdminPricingRoutes(app, options) { }); app.post('/inventory/:id/valuation', async (request, reply) => { + await authorizeUnsafeRequest?.(request.cookies[sessionCookieName], request.headers['x-csrf-token']); const valuation = await pricingService.calculate({ sellerId: request.admin.sellerId, inventoryItemId: request.params.id }); return reply.code(200).send({ data: toAdminMarketValuationDto(valuation) }); }); diff --git a/apps/api/tests/http/pricing-routes.test.js b/apps/api/tests/http/pricing-routes.test.js index 4ff6712..9057c3d 100644 --- a/apps/api/tests/http/pricing-routes.test.js +++ b/apps/api/tests/http/pricing-routes.test.js @@ -24,5 +24,5 @@ test('accepts a protected pricing observation and valuation request', async (tes assert.equal(observationResponse.statusCode, 201); assert.equal(valuationResponse.statusCode, 200); assert.equal(valuationResponse.json().data.valueMinor, 2500); - assert.equal(csrfChecks, 1); + assert.equal(csrfChecks, 2); }); \ No newline at end of file diff --git a/apps/web/src/pages/AdminHomePage.vue b/apps/web/src/pages/AdminHomePage.vue index ffbc4a3..e625f47 100644 --- a/apps/web/src/pages/AdminHomePage.vue +++ b/apps/web/src/pages/AdminHomePage.vue @@ -9,6 +9,7 @@ import { createAdminListing, listAdminListings, markListingPending, publishAdmin import { createAdminSale, listAdminSales, voidAdminSale } from '@/services/admin-sale-service.js'; import { createAdminAcquisition, listAdminAcquisitions } from '@/services/admin-acquisition-service.js'; import { getAdminDashboard } from '@/services/admin-dashboard-service.js'; +import { addAdminPricingObservation, calculateAdminValuation } from '@/services/admin-pricing-service.js'; const { principal } = useAuth(); const products = ref([]); @@ -17,6 +18,7 @@ const listings = ref([]); const sales = ref([]); const acquisitions = ref([]); const dashboard = ref(null); +const valuations = ref({}); const categories = ref([]); const conditions = ref([]); const isLoading = ref(true); @@ -38,6 +40,10 @@ const acquisitionCost = ref(''); const acquisitionShipping = ref(''); const acquisitionTax = ref(''); const acquisitionDate = ref(new Date().toISOString().slice(0, 10)); +const pricingInventoryId = ref(''); +const pricingProvider = ref('manual'); +const pricingSource = ref(''); +const pricingPrice = ref(''); const voidReasons = ref({}); function csrfToken() { @@ -132,6 +138,19 @@ async function recordAcquisition() { finally { isSaving.value = false; } } +async function calculatePricing() { + isSaving.value = true; + try { + const item = inventory.value.find(({ id }) => id === pricingInventoryId.value); + await addAdminPricingObservation({ productId: item.productId, inventoryItemId: item.id, providerId: pricingProvider.value, priceMinor: Number(pricingPrice.value), sourceIdentifier: pricingSource.value, conditionId: item.conditionId }, csrfToken()); + const valuation = await calculateAdminValuation(item.id, csrfToken()); + valuations.value = { ...valuations.value, [item.id]: valuation }; + pricingSource.value = ''; + pricingPrice.value = ''; + } catch (requestError) { error.value = requestError; } + finally { isSaving.value = false; } +} + async function voidSale(saleId) { isSaving.value = true; try { @@ -241,6 +260,21 @@ onMounted(load); +
No physical copies yet.