ReviewOS

stacks/smakelo

B1: Domain models and migrations

#3
Open chris opened this 9 days ago ·
9 days ago

Part of #1. All in app/Models/ via defineModel(), then buddy generate:migrations -> review -> buddy migrate per slice.

New models: Market (country/city, currency, taxMode, locale), Business (type restaurant|cafe|farm, capability flags delivery/pickup/dineIn/shop, address, lat/lng, priceTier, phone, website, slug, marketId, teamId, claimed/partner status; taggable for cuisine), BusinessHour, BusinessPhoto, BusinessReview (rating, text, photos, ownerResponse, helpfulVotes, verifiedOrderId), Favorite, Claim, Table (businessId, label, qrToken), Tab (open table tab + rounds), ModifierGroup/Modifier, OrderItemModifier, Address (customer book), CourierProfile (shiftState, vehicleType - app fields beyond the upstream Courier), DeliveryOffer (courierId, orderId, expiresAt, status), LedgerEntry (party, type, amountCents), PayoutAccount (Stripe Connect account id per party), CsaPlan/CsaSubscription.

Overrides: Product/Category gain businessId; Order gains businessId, scheduledFor, tableId/tabId, marketId, and a fee breakdown in cents.

9 days ago

Half done in 864cdc9.

Built: Market, Business, BusinessHour, BusinessReview, BusinessPhoto, ReviewPhoto, ModifierGroup, Modifier, OrderItemModifier, plus a Product override adding businessId (the framework's Product belongs to a Category and a Manufacturer and nothing else, which is right for one shop and unusable for a marketplace).

Still to add, and where they land:

  • Order override: businessId, fee breakdown in cents, tip, scheduledFor, tableId/tabId (#7)
  • Table, Tab (#9)
  • CourierProfile, DeliveryOffer (#8)
  • LedgerEntry, PayoutAccount (#11)
  • CsaPlan, CsaSubscription (#10)
  • Address, Favorite, Claim (#5)
9 days ago

Two more models landed with the work they were needed for.

  • CsaPlan / CsaSubscription (#10): a share is a claim on the season rather than a recurring order, so the plan promises a size, a cadence and a packing day and deliberately promises no contents. Pausing is a first-class state with a date on it.
  • Address (#12): saved delivery addresses with coordinates, because the fee depends on where the box goes.

A trap worth writing down: schema.number() generates an INTEGER column. For a coordinate that is catastrophic and silent, since a latitude rounded to a whole degree is sixty miles out. schema.float() is what produces REAL, which is what Business.latitude already used. Caught by comparing the generated migration against the existing businesses table before it shipped.

Also fixed: deleted_at was on the Business model and nothing read it, so a soft delete was honoured by no read path at all. Every one now filters it (#16).

Not built, and why: CourierProfile, DeliveryOffer and PayoutAccount. The first two model a dispatch cascade (offer, accept, decline, timeout) which needs couriers who can accept, and this demo has no courier accounts; dispatch currently assigns the nearest free courier directly. PayoutAccount waits on Stripe Connect onboarding, which waits on test keys (#12). Each would be a table with no behaviour behind it, which is worse than not having it.

Sign in to comment on this issue.