forked from erp-dev/erp
248 lines
8.9 KiB
Python
248 lines
8.9 KiB
Python
from django.db import transaction
|
|
from django.db.models import Q
|
|
from django.utils import timezone
|
|
from rest_framework import serializers
|
|
from rest_framework.exceptions import PermissionDenied, ValidationError
|
|
from rest_framework import filters
|
|
from rest_framework.response import Response
|
|
|
|
from flower.viewsets import LimitedModelViewSet
|
|
from basic_info import models as basic_models
|
|
from basic_info.services import CustomerVisibilityService
|
|
|
|
|
|
def _parse_bool(value: str | None) -> bool | None:
|
|
if value is None:
|
|
return None
|
|
normalized = value.strip().lower()
|
|
if normalized in {"1", "true", "yes", "y"}:
|
|
return True
|
|
if normalized in {"0", "false", "no", "n"}:
|
|
return False
|
|
return None
|
|
|
|
|
|
class CustomerAddressSerializer(serializers.ModelSerializer):
|
|
merchant_name = serializers.CharField(source="merchant.name", read_only=True)
|
|
customer_name = serializers.CharField(source="customer.name", read_only=True)
|
|
created_by_name = serializers.CharField(source="created_by.name", read_only=True, allow_null=True)
|
|
deleted_by_name = serializers.CharField(source="deleted_by.name", read_only=True, allow_null=True)
|
|
|
|
class Meta:
|
|
model = basic_models.CustomerAddress
|
|
fields = [
|
|
"id",
|
|
"merchant",
|
|
"merchant_name",
|
|
"customer",
|
|
"customer_name",
|
|
"address",
|
|
"contact_name",
|
|
"contact_phone",
|
|
"area",
|
|
"coordinates",
|
|
"is_default",
|
|
"remark",
|
|
"extra",
|
|
"created_by",
|
|
"created_by_name",
|
|
"deleted_at",
|
|
"deleted_by",
|
|
"deleted_by_name",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = [
|
|
"id",
|
|
"merchant",
|
|
"merchant_name",
|
|
"customer_name",
|
|
"created_by",
|
|
"created_by_name",
|
|
"deleted_at",
|
|
"deleted_by",
|
|
"deleted_by_name",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
|
|
def validate_customer(self, customer):
|
|
request = self.context["request"]
|
|
view = self.context["view"]
|
|
|
|
if self.instance is not None and customer.id != self.instance.customer_id:
|
|
raise serializers.ValidationError("不允许修改客户")
|
|
|
|
if not view.can_access_customer(customer, request.user):
|
|
raise serializers.ValidationError("客户不存在或不可见")
|
|
return customer
|
|
|
|
|
|
class CustomerAddressViewSet(LimitedModelViewSet):
|
|
"""
|
|
客户地址 CRUD。
|
|
|
|
查询参数:
|
|
- customer: 客户ID
|
|
- customer_name: 客户名称模糊查询
|
|
- address: 地址模糊查询
|
|
- contact_name: 联系人模糊查询
|
|
- contact_phone: 联系电话模糊查询
|
|
- area: 地区模糊查询
|
|
- is_default: 是否默认地址 true/false
|
|
- include_deleted: 是否包含软删除记录 true/false
|
|
- only_deleted: 是否仅查询软删除记录 true/false
|
|
- search: 客户名/地址/联系人/电话/地区/备注模糊搜索
|
|
- ordering: id/-id/created_at/-created_at/updated_at/-updated_at/is_default/-is_default
|
|
"""
|
|
|
|
queryset = basic_models.CustomerAddress.objects.all()
|
|
serializer_class = CustomerAddressSerializer
|
|
filter_backends = [filters.OrderingFilter]
|
|
ordering_fields = ["id", "created_at", "updated_at", "is_default"]
|
|
ordering = ["-is_default", "-created_at", "-id"]
|
|
|
|
def get_employee(self):
|
|
return getattr(self.request.user, "employee", None)
|
|
|
|
def can_view_all_customers(self, user) -> bool:
|
|
return user.is_superuser or user.has_perm("basic_info.view_all_customers")
|
|
|
|
def can_access_customer(self, customer, user) -> bool:
|
|
if user.is_superuser:
|
|
return True
|
|
|
|
emp = getattr(user, "employee", None)
|
|
if emp is None or emp.merchant_id != customer.merchant_id:
|
|
return False
|
|
|
|
if user.has_perm("basic_info.view_all_customers"):
|
|
return True
|
|
|
|
return CustomerVisibilityService.is_customer_visible_to_employee(customer, user)
|
|
|
|
def get_queryset(self):
|
|
queryset = (
|
|
super()
|
|
.get_queryset()
|
|
.select_related("merchant", "customer", "created_by", "deleted_by")
|
|
)
|
|
user = self.request.user
|
|
|
|
if not user.is_superuser:
|
|
emp = self.get_employee()
|
|
if emp is None or emp.merchant_id is None:
|
|
return queryset.none()
|
|
|
|
queryset = queryset.filter(merchant=emp.merchant)
|
|
if not user.has_perm("basic_info.view_all_customers"):
|
|
customer_queryset = CustomerVisibilityService.filter_customers_for_employee(
|
|
basic_models.Customer.objects.filter(merchant=emp.merchant),
|
|
user,
|
|
)
|
|
queryset = queryset.filter(customer__in=customer_queryset)
|
|
|
|
only_deleted = _parse_bool(self.request.query_params.get("only_deleted"))
|
|
include_deleted = _parse_bool(self.request.query_params.get("include_deleted"))
|
|
if only_deleted:
|
|
queryset = queryset.filter(deleted_at__isnull=False)
|
|
elif not include_deleted:
|
|
queryset = queryset.filter(deleted_at__isnull=True)
|
|
|
|
customer = self.request.query_params.get("customer")
|
|
if customer:
|
|
if not customer.strip().isdigit():
|
|
return queryset.none()
|
|
queryset = queryset.filter(customer_id=customer)
|
|
|
|
customer_name = (self.request.query_params.get("customer_name") or "").strip()
|
|
if customer_name:
|
|
queryset = queryset.filter(customer__name__icontains=customer_name)
|
|
|
|
address = (self.request.query_params.get("address") or "").strip()
|
|
if address:
|
|
queryset = queryset.filter(address__icontains=address)
|
|
|
|
contact_name = (self.request.query_params.get("contact_name") or "").strip()
|
|
if contact_name:
|
|
queryset = queryset.filter(contact_name__icontains=contact_name)
|
|
|
|
contact_phone = (self.request.query_params.get("contact_phone") or "").strip()
|
|
if contact_phone:
|
|
queryset = queryset.filter(contact_phone__icontains=contact_phone)
|
|
|
|
area = (self.request.query_params.get("area") or "").strip()
|
|
if area:
|
|
queryset = queryset.filter(area__icontains=area)
|
|
|
|
is_default = _parse_bool(self.request.query_params.get("is_default"))
|
|
if is_default is not None:
|
|
queryset = queryset.filter(is_default=is_default)
|
|
|
|
search = (self.request.query_params.get("search") or "").strip()
|
|
if search:
|
|
queryset = queryset.filter(
|
|
Q(customer__name__icontains=search)
|
|
| Q(address__icontains=search)
|
|
| Q(contact_name__icontains=search)
|
|
| Q(contact_phone__icontains=search)
|
|
| Q(area__icontains=search)
|
|
| Q(remark__icontains=search)
|
|
)
|
|
|
|
return queryset.distinct()
|
|
|
|
def _unset_other_default_addresses(self, address: basic_models.CustomerAddress):
|
|
if not address.is_default or address.deleted_at is not None:
|
|
return
|
|
basic_models.CustomerAddress.objects.filter(
|
|
merchant=address.merchant,
|
|
customer=address.customer,
|
|
deleted_at__isnull=True,
|
|
is_default=True,
|
|
).exclude(id=address.id).update(is_default=False)
|
|
|
|
@transaction.atomic
|
|
def perform_create(self, serializer):
|
|
customer = serializer.validated_data["customer"]
|
|
emp = self.get_employee()
|
|
if not self.request.user.is_superuser and emp is None:
|
|
raise PermissionDenied("当前用户未绑定员工")
|
|
|
|
address = serializer.save(
|
|
merchant=customer.merchant,
|
|
created_by=emp,
|
|
)
|
|
self._unset_other_default_addresses(address)
|
|
|
|
@transaction.atomic
|
|
def perform_update(self, serializer):
|
|
address = serializer.save()
|
|
self._unset_other_default_addresses(address)
|
|
|
|
@transaction.atomic
|
|
def perform_destroy(self, instance):
|
|
if instance.deleted_at is not None:
|
|
return
|
|
|
|
emp = self.get_employee()
|
|
if not self.request.user.is_superuser and emp is None:
|
|
raise PermissionDenied("当前用户未绑定员工")
|
|
|
|
instance.deleted_at = timezone.now()
|
|
instance.deleted_by = emp
|
|
instance.is_default = False
|
|
instance.save(update_fields=["deleted_at", "deleted_by", "is_default", "updated_at"])
|
|
|
|
@transaction.atomic
|
|
def set_default(self, request, pk: int):
|
|
address = self.get_object()
|
|
if address.deleted_at is not None:
|
|
raise ValidationError("已删除的客户地址不能设为默认地址")
|
|
|
|
address.is_default = True
|
|
address.save(update_fields=["is_default", "updated_at"])
|
|
self._unset_other_default_addresses(address)
|
|
serializer = self.get_serializer(address)
|
|
return Response(serializer.data)
|