fixes to purchase_email and readme update
This commit is contained in:
176
README.md
176
README.md
@@ -7,7 +7,11 @@ This README aims to document functionality of backend as well as required steps
|
|||||||
- [First Steps](#first-steps)
|
- [First Steps](#first-steps)
|
||||||
- [Load location data](#load-location-data)
|
- [Load location data](#load-location-data)
|
||||||
- [Load taxonomy data](#load-taxonomy-data)
|
- [Load taxonomy data](#load-taxonomy-data)
|
||||||
- [Endpoints](#endpoints)
|
- [Company Endpoints](#company-endpoints)
|
||||||
|
- [Product Endpoints](#product-endpoints)
|
||||||
|
- [Core Endpoints](#core-endpoints)
|
||||||
|
- [History Endpoints](#history-endpoints)
|
||||||
|
- [Stats Endpoints](#stats-endpoints)
|
||||||
- [Shop Integrations](#shop-integrations)
|
- [Shop Integrations](#shop-integrations)
|
||||||
- [WooCommerce](#woocommerce)
|
- [WooCommerce](#woocommerce)
|
||||||
- [Product Search](#product-search)
|
- [Product Search](#product-search)
|
||||||
@@ -57,25 +61,158 @@ This data serves as initial Tags
|
|||||||
To load initial set of tags: `python manage.py addtaxonomy`
|
To load initial set of tags: `python manage.py addtaxonomy`
|
||||||
|
|
||||||
|
|
||||||
## Endpoints
|
## Company Endpoints
|
||||||
|
|
||||||
|
### CompanyViewSet
|
||||||
|
|
||||||
|
Queryset: validated Company instances only
|
||||||
|
|
||||||
|
Permissions:
|
||||||
|
|
||||||
|
- anon user: safe methods
|
||||||
|
- auth user: full access where user is company creator
|
||||||
|
|
||||||
|
### MyCompanyViewSet
|
||||||
|
|
||||||
|
Queryset: Company instances where user is creator
|
||||||
|
|
||||||
|
Permissions:
|
||||||
|
|
||||||
|
- anon user: no access
|
||||||
|
- auth user: full access
|
||||||
|
|
||||||
|
### AdminCompanyViewSet
|
||||||
|
|
||||||
|
Queryset: all Company instances, validated or not
|
||||||
|
|
||||||
|
Permissions: only accesible to authenticated users with role `SITE_ADMIN`
|
||||||
|
|
||||||
|
|
||||||
## Pagination
|
### random_company_sample
|
||||||
|
|
||||||
By default a `LimitOffsetPagination` pagination is enabled
|
Method view that returns a randome sample of companies
|
||||||
|
|
||||||
Examples: `http://127.0.0.1:8000/api/v1/products/?limit=10&offset=0`
|
By default it returns 6 instances, but can be customized through parameter `size`
|
||||||
|
|
||||||
The response data has the following keys:
|
|
||||||
```
|
## Product Endpoints
|
||||||
dict_keys(['count', 'next', 'previous', 'results'])
|
|
||||||
```
|
### ProductViewSet
|
||||||
|
|
||||||
|
Endpoint url: `/api/v1/products/`
|
||||||
|
|
||||||
|
Queryset: active Product instances only
|
||||||
|
|
||||||
|
Permissions:
|
||||||
|
|
||||||
|
- anon user: safe methods
|
||||||
|
- auth user: full access where user is product creator
|
||||||
|
|
||||||
|
### MyProductsViewSet
|
||||||
|
|
||||||
|
Endpoint url: `/api/v1/my_products/`
|
||||||
|
|
||||||
|
Queryset: Product instances where user is creator
|
||||||
|
|
||||||
|
Permissions:
|
||||||
|
|
||||||
|
- anon user: no access
|
||||||
|
- auth user: full access
|
||||||
|
|
||||||
|
|
||||||
|
### AdminProductsViewSet
|
||||||
|
|
||||||
|
Endpoint url: `/api/v1/admin_products/`
|
||||||
|
|
||||||
|
Queryset: all Product instances, acgtive or not
|
||||||
|
|
||||||
|
Permissions: only accesible to authenticated users with role `SITE_ADMIN`
|
||||||
|
|
||||||
|
### load_coop_products [POST]
|
||||||
|
|
||||||
|
Endpoint url: `/api/v1/load_products/`
|
||||||
|
|
||||||
|
Method view that reads a CSV file.
|
||||||
|
|
||||||
|
### product_search [GET]
|
||||||
|
|
||||||
|
Endpoint url: `/api/v1/search_products/`
|
||||||
|
|
||||||
|
Allows searching of Products to all users
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- q: used for search [MANDATORY]
|
||||||
|
- limit: max number of returned instances [OPTIONAL]
|
||||||
|
- offset: where to start counting results [OPTIONAL]
|
||||||
|
- shipping_cost: true/false
|
||||||
|
- discount: true/false
|
||||||
|
- category: string
|
||||||
|
- tags: string
|
||||||
|
- order: string (newest/oldest)
|
||||||
|
- price_min: int
|
||||||
|
- price_max: int
|
||||||
|
|
||||||
|
|
||||||
|
### purchase_email [POST]
|
||||||
|
|
||||||
|
Endpoint url: `/api/v1/purchase_email/`
|
||||||
|
|
||||||
|
Sends email to company manager about the product that the user wants to purchase, and sends confirmation email to user.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- email: mandatory for anonymous users
|
||||||
|
- telephone
|
||||||
|
- company
|
||||||
|
- product
|
||||||
|
- comment
|
||||||
|
|
||||||
|
## Core Endpoints
|
||||||
|
|
||||||
|
### CustomUserViewSet
|
||||||
|
|
||||||
|
Endpoint url: `/api/v1/users/`
|
||||||
|
|
||||||
|
Queryset: all CustomUser instances
|
||||||
|
|
||||||
|
Permissions:
|
||||||
|
|
||||||
|
- anon user: only POST to register new user
|
||||||
|
- auth user: no access
|
||||||
|
- admin user: full access
|
||||||
|
|
||||||
|
### ChangeUserPasswordView
|
||||||
|
|
||||||
|
Ednpoint url: `/api/v1/user/change_password/<int:pk>/`
|
||||||
|
|
||||||
|
Permissions: only accessible for your own user instance
|
||||||
|
|
||||||
|
|
||||||
|
### UpdateUserView
|
||||||
|
|
||||||
|
Endpoint url: `/api/v1/user/update/`
|
||||||
|
|
||||||
|
Permissions: only accessible for your own user instance
|
||||||
|
|
||||||
|
|
||||||
|
### my_user [GET]
|
||||||
|
|
||||||
|
Endpoint url: `/api/v1/my_user/`
|
||||||
|
|
||||||
|
Returns instance of authenticated user
|
||||||
|
|
||||||
|
### load_coop_managers [POST]
|
||||||
|
|
||||||
|
Ednpoint url: `/api/v1/load_coops/`
|
||||||
|
|
||||||
|
For each row it creates a Company instance, and a user instance linked to the company, with role `COOP_MANAGER`
|
||||||
|
|
||||||
### User Management
|
### User Management
|
||||||
|
|
||||||
Creation:
|
Creation:
|
||||||
|
|
||||||
- endpoint: /api/v1/users/
|
- endpoint: `/api/v1/users/`
|
||||||
- method: GET
|
- method: GET
|
||||||
- payload:
|
- payload:
|
||||||
```json
|
```json
|
||||||
@@ -149,32 +286,21 @@ To create user:
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Companies
|
## History Endpoints
|
||||||
|
|
||||||
Endpoint url: `/api/v1/companies/`
|
|
||||||
|
|
||||||
To get company linked to authenticated user: `/api/v1/my_company/`
|
|
||||||
|
|
||||||
### Products
|
|
||||||
|
|
||||||
Endpoint url: `/api/v1/products/`
|
|
||||||
|
|
||||||
To get products linked to authenticated user: `/api/v1/my_products/`
|
|
||||||
|
|
||||||
### History
|
|
||||||
|
|
||||||
Endpoint url: `/api/v1/history/`:
|
Endpoint url: `/api/v1/history/`:
|
||||||
|
|
||||||
Historical records about product importation
|
Historical records about product importation
|
||||||
|
|
||||||
|
|
||||||
### Stats
|
## Stats Endpoints
|
||||||
|
|
||||||
Endpoint url: `/api/v1/stats/`
|
Endpoint url: `/api/v1/stats/`
|
||||||
|
|
||||||
logs about user interaction with products links
|
logs about user interaction with products links
|
||||||
|
|
||||||
|
|
||||||
### Locations
|
## Location Endpoints
|
||||||
|
|
||||||
Location ednpoints:
|
Location ednpoints:
|
||||||
|
|
||||||
|
|||||||
@@ -1210,12 +1210,13 @@ class PurchaseEmailTest(APITestCase):
|
|||||||
self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
|
self.password = ''.join(random.choices(string.ascii_uppercase, k = 10))
|
||||||
self.user = CustomUserFactory(email=self.email, is_active=True)
|
self.user = CustomUserFactory(email=self.email, is_active=True)
|
||||||
self.user.set_password(self.password)
|
self.user.set_password(self.password)
|
||||||
# self.user.role = 'SITE_ADMIN'
|
|
||||||
self.user.save()
|
self.user.save()
|
||||||
|
|
||||||
def test_anon_user_can_use(self):
|
def test_anon_user_can_use(self):
|
||||||
|
|
||||||
company = CompanyFactory()
|
company = CompanyFactory()
|
||||||
|
self.user.role = 'COOP_MANAGER'
|
||||||
|
self.user.company = company
|
||||||
|
self.user.save()
|
||||||
product = ProductFactory(company=company)
|
product = ProductFactory(company=company)
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
@@ -1225,8 +1226,29 @@ class PurchaseEmailTest(APITestCase):
|
|||||||
'product': product.id,
|
'product': product.id,
|
||||||
'comment': '',
|
'comment': '',
|
||||||
}
|
}
|
||||||
response = self.client.post(self.endpoint, json=data)
|
response = self.client.post(self.endpoint, data=data, format='json')
|
||||||
import ipdb; ipdb.set_trace()
|
# assertions
|
||||||
|
self.assertEquals(response.status_code, 200)
|
||||||
|
self.assertEquals(2, len(mail.outbox))
|
||||||
|
|
||||||
|
def test_auth_user_can_use(self):
|
||||||
|
company = CompanyFactory()
|
||||||
|
self.user.role = 'COOP_MANAGER'
|
||||||
|
self.user.company = company
|
||||||
|
self.user.save()
|
||||||
|
product = ProductFactory(company=company)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'telephone': '123123123',
|
||||||
|
'company': company.id,
|
||||||
|
'product': product.id,
|
||||||
|
'comment': '',
|
||||||
|
}
|
||||||
|
# Authenticate
|
||||||
|
token = get_tokens_for_user(self.user)
|
||||||
|
self.client.credentials(HTTP_AUTHORIZATION=f"Bearer {token['access']}")
|
||||||
|
|
||||||
|
response = self.client.post(self.endpoint, data=data, format='json')
|
||||||
# assertions
|
# assertions
|
||||||
self.assertEquals(response.status_code, 200)
|
self.assertEquals(response.status_code, 200)
|
||||||
self.assertEquals(2, len(mail.outbox))
|
self.assertEquals(2, len(mail.outbox))
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import json
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.core import serializers
|
from django.core import serializers
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.template.loader import render_to_string
|
||||||
|
from django.core.mail import EmailMessage
|
||||||
|
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
@@ -24,6 +26,7 @@ from dal import autocomplete
|
|||||||
from products.models import Product, CategoryTag
|
from products.models import Product, CategoryTag
|
||||||
from products.serializers import ProductSerializer, TagFilterSerializer, SearchResultSerializer
|
from products.serializers import ProductSerializer, TagFilterSerializer, SearchResultSerializer
|
||||||
from companies.models import Company
|
from companies.models import Company
|
||||||
|
from stats.models import StatsLog
|
||||||
from back_latienda.permissions import IsCreator, IsSiteAdmin, ReadOnly
|
from back_latienda.permissions import IsCreator, IsSiteAdmin, ReadOnly
|
||||||
from .utils import extract_search_filters, find_related_products_v6, product_loader, find_related_products_v7
|
from .utils import extract_search_filters, find_related_products_v6, product_loader, find_related_products_v7
|
||||||
from utils.tag_serializers import TaggitSerializer
|
from utils.tag_serializers import TaggitSerializer
|
||||||
@@ -247,19 +250,21 @@ class CategoryTagAutocomplete(autocomplete.Select2QuerySetView):
|
|||||||
return qs # [x.label for x in qs]
|
return qs # [x.label for x in qs]
|
||||||
|
|
||||||
|
|
||||||
@permission_classes([AllowAny,])
|
|
||||||
@api_view(['POST'])
|
@api_view(['POST'])
|
||||||
|
@permission_classes([AllowAny,])
|
||||||
def purchase_email(request):
|
def purchase_email(request):
|
||||||
"""Notify coop manager and user about item purchase
|
"""Notify coop manager and user about item purchase
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data = json.loads(request.body)
|
data = json.loads(request.body)
|
||||||
# check data
|
# check data
|
||||||
|
if request.user.is_anonymous and 'email' not in data:
|
||||||
|
return Response({"error": "Anonymous users must include an email parameter value"}, status=status.HTTP_406_NOT_ACCEPTABLE)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for param in ('email', 'telephone', 'company', 'product', 'comment'):
|
for param in ('telephone', 'company', 'product', 'comment'):
|
||||||
assert(param in data.keys())
|
assert(param in data.keys())
|
||||||
except:
|
except:
|
||||||
return Response({"error": "Required parameters for anonymous user: email, telephone"}, status=status.HTTP_406_NOT_ACCEPTABLE)
|
return Response({"error": "Required parameters for anonymous user: telephone, company, product, comment"}, status=status.HTTP_406_NOT_ACCEPTABLE)
|
||||||
|
|
||||||
if request.user.is_anonymous:
|
if request.user.is_anonymous:
|
||||||
email = data.get('email')
|
email = data.get('email')
|
||||||
@@ -273,7 +278,7 @@ def purchase_email(request):
|
|||||||
return Response({"error": "Invalid value for company"}, status=status.HTTP_406_NOT_ACCEPTABLE)
|
return Response({"error": "Invalid value for company"}, status=status.HTTP_406_NOT_ACCEPTABLE)
|
||||||
# get company manager
|
# get company manager
|
||||||
manager = User.objects.filter(company=company).first()
|
manager = User.objects.filter(company=company).first()
|
||||||
if not manager and manager.role != 'COOP_MANAGER':
|
if not manager or manager.role != 'COOP_MANAGER':
|
||||||
return Response({"error": "Company has no managing user"}, status=status.HTTP_406_NOT_ACCEPTABLE)
|
return Response({"error": "Company has no managing user"}, status=status.HTTP_406_NOT_ACCEPTABLE)
|
||||||
# get product
|
# get product
|
||||||
product = Product.objects.filter(id=data['product'], company=company).first()
|
product = Product.objects.filter(id=data['product'], company=company).first()
|
||||||
@@ -297,19 +302,17 @@ def purchase_email(request):
|
|||||||
'product': product,
|
'product': product,
|
||||||
})
|
})
|
||||||
subject = 'Confirmación de contacto con vendedor'
|
subject = 'Confirmación de contacto con vendedor'
|
||||||
email = EmailMessage(subject, message, to=[request.user.email])
|
email = EmailMessage(subject, user_message, to=[email])
|
||||||
email.send()
|
email.send()
|
||||||
logging.info(f"Purchase Contact confirmation email sent to {request.user.email}")
|
logging.info(f"Purchase Contact confirmation email sent to {email}")
|
||||||
|
|
||||||
# create statslog instance to register interaction
|
# create statslog instance to register interaction
|
||||||
stats_data = {
|
stats_data = {
|
||||||
'action_object': instance,
|
'action_object': product,
|
||||||
'user': None,
|
'user': request.user if request.user.is_authenticated else None,
|
||||||
'anonymous': True,
|
'anonymous': request.user.is_anonymous,
|
||||||
'ip_address': client_ip,
|
|
||||||
'geo': g.geos(client_ip),
|
|
||||||
'contact': True,
|
'contact': True,
|
||||||
'shop': instance.shop,
|
'shop': company.shop,
|
||||||
}
|
}
|
||||||
StatsLog.objects.create(**stats_data)
|
StatsLog.objects.create(**stats_data)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user