feat(#49): add product provenance (suppliers, organizations, geography)
- Country, Department, Municipality models (municipality unique per department) - Organization and Supplier models, Supplier links to organization and municipality - Product.suppliers M2M to link products to one or more suppliers - Authenticated CRUD APIs for organizations, suppliers, countries, departments and municipalities (write only for administrators) - product_provenance domain data embedded in sale/catalog summaries (public and authenticated) - seed_geography endpoint (admin, idempotent) and scripts/seed_geography.py using pycountry + DANE municipalities CSV - TDD tests for models, API permissions/CRUD and summary provenance data
This commit is contained in:
@@ -17,6 +17,8 @@ from .payments import (
|
||||
PaymentMethodSerializer,
|
||||
)
|
||||
from .store_settings import StoreSettingsSerializer
|
||||
from .geography import CountrySerializer, DepartmentSerializer, MunicipalitySerializer
|
||||
from .provenance import OrganizationSerializer, SupplierSerializer
|
||||
|
||||
__all__ = [
|
||||
# Catalogue Images
|
||||
@@ -42,4 +44,11 @@ __all__ = [
|
||||
"PaymentMethodSerializer",
|
||||
# Store Settings
|
||||
"StoreSettingsSerializer",
|
||||
# Geography
|
||||
"CountrySerializer",
|
||||
"DepartmentSerializer",
|
||||
"MunicipalitySerializer",
|
||||
# Provenance
|
||||
"OrganizationSerializer",
|
||||
"SupplierSerializer",
|
||||
]
|
||||
|
||||
21
tienda_ilusion/don_confiao/serializers/geography.py
Normal file
21
tienda_ilusion/don_confiao/serializers/geography.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from ..models.geography import Country, Department, Municipality
|
||||
|
||||
|
||||
class CountrySerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Country
|
||||
fields = ["id", "name", "code"]
|
||||
|
||||
|
||||
class DepartmentSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Department
|
||||
fields = ["id", "name", "country"]
|
||||
|
||||
|
||||
class MunicipalitySerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Municipality
|
||||
fields = ["id", "name", "department", "country"]
|
||||
@@ -1,10 +1,14 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from ..models.products import Product, ProductCategory
|
||||
from ..models.provenance import Supplier
|
||||
|
||||
|
||||
class ProductSerializer(serializers.ModelSerializer):
|
||||
catalogue_images = serializers.SerializerMethodField()
|
||||
suppliers = serializers.PrimaryKeyRelatedField(
|
||||
many=True, queryset=Supplier.objects.all(), required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Product
|
||||
@@ -15,6 +19,7 @@ class ProductSerializer(serializers.ModelSerializer):
|
||||
"price",
|
||||
"measuring_unit",
|
||||
"categories",
|
||||
"suppliers",
|
||||
"external_id",
|
||||
"catalogue_images",
|
||||
]
|
||||
|
||||
93
tienda_ilusion/don_confiao/serializers/provenance.py
Normal file
93
tienda_ilusion/don_confiao/serializers/provenance.py
Normal file
@@ -0,0 +1,93 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from ..models.geography import Country, Department, Municipality
|
||||
from ..models.provenance import Organization, Supplier
|
||||
|
||||
|
||||
class CountryBriefSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Country
|
||||
fields = ["id", "name", "code"]
|
||||
|
||||
|
||||
class DepartmentBriefSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Department
|
||||
fields = ["id", "name"]
|
||||
|
||||
|
||||
class MunicipalityBriefSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Municipality
|
||||
fields = ["id", "name"]
|
||||
|
||||
|
||||
class OrganizationSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Organization
|
||||
fields = [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"website",
|
||||
"contact_email",
|
||||
"contact_phone",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
|
||||
class OrganizationBriefSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Organization
|
||||
fields = [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"website",
|
||||
"contact_email",
|
||||
"contact_phone",
|
||||
]
|
||||
|
||||
|
||||
class SupplierSerializer(serializers.ModelSerializer):
|
||||
organization_detail = OrganizationBriefSerializer(
|
||||
source="organization", read_only=True
|
||||
)
|
||||
municipality_detail = MunicipalityBriefSerializer(
|
||||
source="municipality", read_only=True
|
||||
)
|
||||
products = serializers.PrimaryKeyRelatedField(
|
||||
many=True, read_only=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Supplier
|
||||
fields = [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"organization",
|
||||
"organization_detail",
|
||||
"municipality",
|
||||
"municipality_detail",
|
||||
"website",
|
||||
"contact_email",
|
||||
"contact_phone",
|
||||
"products",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
|
||||
class SupplierBriefSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Supplier
|
||||
fields = [
|
||||
"id",
|
||||
"name",
|
||||
"description",
|
||||
"website",
|
||||
"contact_email",
|
||||
"contact_phone",
|
||||
]
|
||||
@@ -1,6 +1,7 @@
|
||||
from rest_framework import serializers
|
||||
from django.urls import reverse
|
||||
|
||||
from ..models.products import Product
|
||||
from ..models.sales import (
|
||||
Sale,
|
||||
SaleLine,
|
||||
@@ -10,6 +11,7 @@ from ..models.sales import (
|
||||
)
|
||||
from .products import ListProductSerializer
|
||||
from .customers import ListCustomerSerializer
|
||||
from ..services.provenance import build_product_provenance
|
||||
|
||||
|
||||
class PublicSummaryLinkMixin:
|
||||
@@ -23,6 +25,16 @@ class PublicSummaryLinkMixin:
|
||||
return path
|
||||
|
||||
|
||||
class ProvenanceSummaryMixin:
|
||||
def get_product_provenance(self, obj):
|
||||
products = Product.objects.filter(
|
||||
saleline__sale=obj
|
||||
).distinct()
|
||||
return build_product_provenance(
|
||||
products, self.context.get("request")
|
||||
)
|
||||
|
||||
|
||||
class SaleLineSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = SaleLine
|
||||
@@ -101,14 +113,26 @@ class SummarySaleLineSerializer(serializers.ModelSerializer):
|
||||
fields = ["product", "quantity", "unit_price", "description"]
|
||||
|
||||
|
||||
class SaleSummarySerializer(PublicSummaryLinkMixin, serializers.ModelSerializer):
|
||||
class SaleSummarySerializer(
|
||||
PublicSummaryLinkMixin, ProvenanceSummaryMixin, serializers.ModelSerializer
|
||||
):
|
||||
customer = ListCustomerSerializer()
|
||||
lines = SummarySaleLineSerializer(many=True, source="saleline_set")
|
||||
link = serializers.SerializerMethodField()
|
||||
product_provenance = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Sale
|
||||
fields = ["id", "code", "date", "customer", "payment_method", "lines", "link"]
|
||||
fields = [
|
||||
"id",
|
||||
"code",
|
||||
"date",
|
||||
"customer",
|
||||
"payment_method",
|
||||
"lines",
|
||||
"link",
|
||||
"product_provenance",
|
||||
]
|
||||
|
||||
|
||||
class CatalogSummarySaleLineSerializer(serializers.ModelSerializer):
|
||||
@@ -119,18 +143,31 @@ class CatalogSummarySaleLineSerializer(serializers.ModelSerializer):
|
||||
fields = ["product", "quantity", "unit_price", "description"]
|
||||
|
||||
|
||||
class CatalogProvenanceSummaryMixin(ProvenanceSummaryMixin):
|
||||
def get_product_provenance(self, obj):
|
||||
products = Product.objects.filter(
|
||||
catalogsaleline__catalog_sale=obj
|
||||
).distinct()
|
||||
return build_product_provenance(
|
||||
products, self.context.get("request")
|
||||
)
|
||||
|
||||
|
||||
class CatalogSaleSummarySerializer(
|
||||
PublicSummaryLinkMixin, serializers.ModelSerializer
|
||||
PublicSummaryLinkMixin,
|
||||
CatalogProvenanceSummaryMixin,
|
||||
serializers.ModelSerializer,
|
||||
):
|
||||
customer = ListCustomerSerializer()
|
||||
lines = CatalogSummarySaleLineSerializer(
|
||||
many=True, source="catalogsaleline_set"
|
||||
)
|
||||
link = serializers.SerializerMethodField()
|
||||
product_provenance = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = CatalogSale
|
||||
fields = ["id", "code", "date", "customer", "lines", "link"]
|
||||
fields = ["id", "code", "date", "customer", "lines", "link", "product_provenance"]
|
||||
|
||||
|
||||
class SaleForRenconciliationSerializer(serializers.Serializer):
|
||||
|
||||
Reference in New Issue
Block a user