import type Log from 'bunyan'; import type { PrismaClient, Want, User, Listing, Offer, } from '@prisma/client'; import groupBy from 'lodash/groupBy'; import sortBy from 'lodash/sortBy'; import { Context } from 'koa'; import { logAxiosError } from './utils'; import { Pg } from './pg'; export enum Status { Pending = 'pending', Accepted = 'accepted', Ordered = 'ordered', Delivered = 'delivered', } export default function createOffers(log: Log, pg: Pg) { async function generate(listingIds: DiscogsListing[], bulkShipping: number, baseShipping: number) { log.info('generateOffers', { listingIds, bulkShipping, baseShipping }); const listings = await pg.listingByIds(listingIds); // log.info({ listings }, 'creating offers for listings') const shipping = 10 + Math.random(); // yolt // const shipping = bulkShipping / listings.length; // how to deal with users who don't want all items in a bundle... const offers = listings.map(l => ({ userId: l.want.user.id, listingId: l.id, wantId: l.want.id, // sellerId: l.seller, status: Status.Pending, item: l.data.price.value, shipping, total: l.data.price.value + shipping, // without mcrates... comparison: l.data.price.value + baseShipping, })) // we must sort the offers by price, // sometimes a seller has multiples of a want // to deal with this we just ignore duplicates on insert // and ensure the cheapest one goes in first const sorted = sortBy(offers, ['item']); const inserts = await pg.offersCreate(sorted); return inserts; } async function accept(ctx: Context) { const { id } = ctx.request.body; ctx.assert(id, 401, 'no id supplied'); // check the user actually has this offer const offer = await pg.userOfferFind(ctx.state.user, id); return pg.offerSetStatus(offer.id, Status.Accepted); } async function bundles() { const bundles = await pg.bundleCandidates(); interface ListingData extends Listing { data: any, want: { user: User }, }; return bundles.map((b: ListingData[]) => { const total = b.reduce((acc, l) => acc + l.data.price.value, 0); const items = b.map(l => ({ id: l.data.id, description: l.data.release.description, originalPrice: l.data.original_price.value, originalCurrency: l.data.original_price.curr_abbr, price: l.data.price.value, currency: l.data.price.currency, condition: l.data.condition, uri: l.data.uri, user: l.want.user, })); const shipping = b[0]?.data.seller.shipping; const bundle = { username: b[0]?.data.seller.username, seller: b[0]?.data.seller.id, shipsFrom: b[0]?.data.ships_from, total, currency: b[0]?.data.price.currency, shipping: '...', items, mentionsAustralia: shipping.toLowerCase().includes('australia'), mentionsWorldwide: shipping.toLowerCase().includes('world'), } return bundle; }); } return { generate, accept, bundles, }; }