Commit graph

32 commits

Author SHA1 Message Date
85fe57d1ca Allow checkout to target selected stores instead of the whole cart
POST /api/v1/checkout/ always converted the customer's entire
multi-store cart into orders. Add an optional store_uuids field to
CheckoutSerializer — when given, checkout() only processes those
stores' cart groups and only clears their items, leaving the rest of
the cart intact for a later checkout. Omitted, behavior is unchanged
(whole cart checks out).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 15:26:16 +03:30
6431c28e0f Restock product inventory when an order is cancelled
checkout() decrements stock_quantity/increments sold_count per order
item, but cancellation never reversed it, so a cancelled order's stock
stayed permanently reduced. Add _restock_order_items(), called from
_transition() on the CANCELLED transition, using F() expressions to
reverse both counters atomically; wrap _transition() itself in
@transaction.atomic so the status change and restock can't partially
apply.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 15:08:02 +03:30
dcf63952b5 Merge pull request 'Return store latitude/longitude in every store response' (#7) from feature/lat-long into master
Reviewed-on: #7
2026-08-31 08:51:01 -04:00
f7a65cb902 Return store latitude/longitude in every store response
Add Store.latitude/Store.longitude properties derived from the
location PointField, and expose them read-only on StoreListSerializer
(inherited by StoreDetailSerializer). Also make them readable on
SellerStoreSerializer, which previously only accepted them as
write-only input.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 16:14:33 +03:30
1d58c52a0d Merge pull request 'Add missing required_alternate_scopes across OAS-gated views' (#6) from fix/required-scopes into master
Reviewed-on: #6
2026-08-31 07:23:26 -04:00
674ca34041 Add missing required_alternate_scopes across OAS-gated views
Several views using IsAuthenticatedOrTokenMatchesOASRequirements were
missing scope entries for methods they actually expose (GET on
list/retrieve-only viewsets, PUT/PATCH/DELETE on ModelViewSets), and
two had a stale POST entry for a method that doesn't exist
(SellerStoreWorkingHoursView, OrderGroupViewSet). Fixes:
- SellerStoreView, SellerStoreWorkingHoursView (apps/stores/views.py)
- AddressViewSet (apps/locations/views.py)
- SellerReviewViewSet (apps/reviews/views.py)
- SellerProductViewSet (apps/catalog/views.py)
- OrderGroupViewSet, OrderViewSet, SellerOrderViewSet,
  NotificationViewSet (apps/orders/views.py)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 14:46:02 +03:30
3d10c897af FIX(winofy): fix required scopes
fix required scopes
2026-08-31 14:16:50 +03:30
ce4fe5a2c9 FIX(winofy): fix required scopes
fix required scopes
2026-08-31 14:11:38 +03:30
dfbbbb2b22 Merge pull request 'Split CartItemView so swagger stops showing broken cart endpoints' (#5) from fix/card-urls into master
Reviewed-on: #5
2026-08-31 05:05:12 -04:00
d4254ac086 Split CartItemView so swagger stops showing broken cart endpoints
CartItemView was registered on both /cart/items/ and
/cart/items/{item_uuid}/, so swagger listed POST/PATCH/DELETE on both
paths even though 3 of those 6 combinations raised a TypeError at
runtime (missing/unexpected item_uuid). Split into CartItemView (POST,
list path) and CartItemDetailView (PATCH/DELETE, detail path).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-31 12:01:38 +03:30
6796922fe6 Merge pull request 'Add search to city/neighborhood endpoints; document filters for swagger' (#4) from feature/filter-stores into master
Reviewed-on: #4
2026-08-23 02:47:04 -04:00
eb5eb38665 Migrate manual query-param filtering to django-filter
django-filter was already installed and set as DEFAULT_FILTER_BACKENDS
but unused everywhere. Replace hand-rolled get_queryset filtering with
FilterSet classes (stores/catalog/locations/reviews) and
filterset_fields (orders status). drf-spectacular auto-documents these
for swagger, so the manual OpenApiParameter declarations for the
migrated fields are removed (stores keeps lat/lng manual since
geo-distance isn't a plain filter).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 17:31:01 +03:30
755d74a9ff Return phone_number and distance_km on store list
phone_number was only exposed on the store detail serializer; add it
to the list too. Also surface the distance annotation (already
computed when lat/lng are passed) as distance_km on both, instead of
using it only for ordering.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 17:04:21 +03:30
fa9a69fff3 Add search to city/neighborhood endpoints; document filters for swagger
Neighborhood filtering was UUID-only; add name-based search on both
GET /api/v1/cities/ and GET /api/v1/neighborhoods/ (the latter also
matching by city name), and document all params for drf-spectacular.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-22 11:25:45 +03:30
2b3fd0fc28 Merge pull request 'Document store list query params for swagger; add filter test coverage' (#3) from feature/filter-stores into master
Reviewed-on: #3
2026-08-19 07:05:21 -04:00
661129d0a1 Document product list query params for swagger; add filter test coverage
Same gap as the stores endpoint: store/category/search filters on
GET /api/v1/products/ already worked but weren't declared to
drf-spectacular.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 14:34:15 +03:30
4073081c25 Document store list query params for swagger; add filter test coverage
Category/neighborhood/search/lat/lng filters on GET /api/v1/stores/
already worked but weren't declared to drf-spectacular, so they never
showed up in swagger.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 14:19:48 +03:30
4aacaabdeb Merge pull request 'Seed demo stores/products with a placeholder image key; doc cleanup' (#2) from feature/minio-integration into master
Reviewed-on: #2
2026-08-18 09:34:03 -04:00
e85b4f19ad merge master 2026-08-18 17:02:18 +03:30
56c07c3678 Seed demo stores/products with a placeholder image key; doc cleanup
- seed_demo_data: every seeded Store.logo/cover_image and Product.image
  now points at a shared placeholder MinIO object_key
  (uploads/c1508b9e-c01f-4892-b24c-c1a949b5b5f5.png) instead of null, so
  demo data exercises the image_url/logo_url/etc. fields end-to-end.
  Note: <field>_url will 404 until that object is actually uploaded to
  the bucket.
- .env.example: drop the redundant MINIO_EXTERNAL_ENDPOINT* lines —
  they already default to MINIO_ENDPOINT/MINIO_USE_HTTPS, and aren't
  read anywhere in this app's actual presign/read path.
- Add MEDIA_INTEGRATION.md: reference doc for the MinIO/presigned-media
  work on this branch (architecture, settings, API shape, existing-data
  caveats, what was verified).
2026-08-18 16:51:19 +03:30
b162a9f31c FIX(winofy): fix MINIO_BUCKET_NAME
fix MINIO_BUCKET_NAME
2026-08-18 13:58:54 +03:30
7ed94df6b3 Merge pull request 'feature/minio-integration' (#1) from feature/minio-integration into master
Reviewed-on: #1
2026-08-18 03:38:48 -04:00
3748da1af7 Add explicit schema types for presigned media URL fields
Swagger inferred these SerializerMethodFields as untyped strings and
warned on generation; annotate each with @extend_schema_field so the
generated OpenAPI schema declares them as nullable URLs.
2026-08-18 10:48:36 +03:30
85598d347a Switch media uploads to presigned MinIO, matching the rest of Winsoo
The prior commit wired MinIO as Django's default storage backend but
kept plain ImageField multipart uploads through the Django backend and
public-bucket direct URLs — not how campaign/advertising/promotions do
it. Rework to match: image fields (ProductCategory.icon, Product.image,
StoreCategory.icon, Store.logo, Store.cover_image) now store an opaque
MinIO object_key (CharField) instead of a Django-managed file.

- utils/clients/minio_client.py — raw Minio SDK client, same shape as
  the other services.
- apps/core/views/media.py — POST /api/media/presign/ mints a 30-minute
  presigned PUT URL + object_key; the client uploads bytes directly to
  MinIO, then submits the object_key on the owning resource.
- apps/core/media.py — presigned_media_url() helper; every serializer
  with an image field now also exposes a `<field>_url` computed from a
  fresh 1-hour presigned GET, rather than trusting a stored/direct URL.
- FRONTEND_GUIDE.md updated to document the new upload flow and field
  shapes (object_key vs `_url`), replacing the stale "no upload
  endpoint yet" note.

Verified against a live local MinIO container: presign → direct PUT
upload → object_key stored on the model → serializer mints a working
presigned GET URL → URL fetches the uploaded bytes. Also exercised the
actual DRF view (POST /api/media/presign/) end-to-end, including the
401 on an unauthenticated request.
2026-08-18 10:42:44 +03:30
c4f15d9c80 Integrate MinIO for media storage
Swaps default media storage from local FileSystemStorage to MinIO via
django-minio-backend, matching the rest of the Winsoo ecosystem
(advertising, campaign, wallet). Points at a local MinIO container
(added to docker-compose.yml) by default since no shared bucket is
provisioned yet. Existing ImageFields pick up the new storage backend
automatically — no model changes needed.

Verified against a live local MinIO container: bucket auto-creates on
first save, object lands under the expected key, URL resolves.
2026-08-17 18:01:29 +03:30
c9b617483d FIX(winofy): add gooyal auth
add gooyal auth
2026-08-15 10:15:28 +03:30
63a116c2b2 Merge branch 'master' of https://git.addwin.ir/addwin/winofy-backend 2026-08-15 09:45:59 +03:30
1409a9cddf FIX(winofy): add gooyal auth
add gooyal auth
2026-08-15 09:44:49 +03:30
d2cc4c2ec6 FIX(winofy): add gooyal auth
add gooyal auth
2026-08-15 09:43:47 +03:30
a223772ffb Add pillow for ImageField support
catalog and stores models use ImageField (icons, logos, product images),
which requires Pillow to be installed.
2026-08-11 13:10:40 +03:30
4a219164b0 Add gunicorn for production WSGI serving 2026-08-11 13:02:03 +03:30
52adc732fa Initial Winofy backend: marketplace core (no payment integration)
Django 6 + DRF resource server against the Gooyal accounts OAuth2 service,
matching the Winsoo ecosystem's conventions. Covers locations, stores,
catalog, cart, checkout/orders (with the multi-store-cart split and the
status stepper), reviews, and notifications, plus a demo-data seed command.

Payment integration (wallet debits, online gateway, seller payouts) is
intentionally left out here — see feature/payment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 14:03:15 +03:30