refactor: organize code by domain-driven design

Refactoriza la estructura del proyecto siguiendo principios de Domain-Driven Design,
organizando serializers, API views y servicios por dominios de negocio.

Cambios principales:

## Serializers (serializers/)
- Dividido serializers.py en módulos por dominio:
  * products.py: ProductSerializer, ListProductSerializer
  * customers.py: CustomerSerializer, ListCustomerSerializer
  * sales.py: SaleSerializer, SaleLineSerializer, CatalogSaleSerializer, etc.
  * payments.py: ReconciliationJarSerializer, PaymentMethodSerializer
  * __init__.py: Exporta todos los serializers para mantener compatibilidad

## API Views (api/)
- Dividido api_views.py en módulos por dominio:
  * products.py: ProductView, ProductsFromTrytonView
  * customers.py: CustomerView, CustomersFromTrytonView
  * sales.py: SaleView, CatalogSaleView, SaleSummary, SalesForTrytonView, SalesToTrytonView
  * payments.py: ReconciliateJarView, ReconciliateJarModelView, PaymentMethodView, SalesForReconciliationView
  * admin.py: AdminCodeValidateView
  * __init__.py: Exporta todas las vistas para facilitar importaciones

## Services Layer (services/tryton/)
- Nueva capa de servicios para lógica de negocio Tryton:
  * client.py: get_tryton_client(), TrytonSale, TrytonLineSale, configuración
  * products.py: ProductTrytonService - sincronización de productos
  * customers.py: CustomerTrytonService - sincronización de clientes
  * sales.py: SaleTrytonService - sincronización de ventas
  * __init__.py: Exporta servicios y utilidades

## Actualización de URLs
- Actualizado urls.py para importar desde nuevos módulos
- Mantiene todas las rutas existentes sin cambios

## Eliminación de archivos antiguos
- Eliminado serializers.py (refactorizado a serializers/)
- Eliminado api_views.py (refactorizado a api/)

## Beneficios
 Cohesión: Código organizado por dominio de negocio
 Separación de responsabilidades: API, Serializers y Services separados
 Mantenibilidad: Archivos más pequeños y enfocados
 Escalabilidad: Fácil agregar nuevos dominios
 Testabilidad: Mejor organización para pruebas por dominio
 Reutilización: Servicios Tryton pueden usarse desde cualquier vista

## Estructura final:
- models/ (ya existía organizado por dominio)
- serializers/ (nuevo, organizado por dominio)
- api/ (nuevo, organizado por dominio)
- services/tryton/ (nuevo, capa de servicios)

Tests: 46 tests pasando ✓
This commit is contained in:
2026-05-29 00:16:18 -05:00
parent f526330f9e
commit 47e87e4204
19 changed files with 824 additions and 629 deletions

View File

@@ -0,0 +1,35 @@
from .products import ProductSerializer, ListProductSerializer
from .customers import CustomerSerializer, ListCustomerSerializer
from .sales import (
SaleSerializer,
SaleLineSerializer,
CatalogSaleSerializer,
CatalogSaleLineSerializer,
SummarySaleLineSerializer,
SaleSummarySerializer,
SaleForRenconciliationSerializer,
)
from .payments import (
ReconciliationJarSerializer,
PaymentMethodSerializer,
)
__all__ = [
# Products
"ProductSerializer",
"ListProductSerializer",
# Customers
"CustomerSerializer",
"ListCustomerSerializer",
# Sales
"SaleSerializer",
"SaleLineSerializer",
"CatalogSaleSerializer",
"CatalogSaleLineSerializer",
"SummarySaleLineSerializer",
"SaleSummarySerializer",
"SaleForRenconciliationSerializer",
# Payments
"ReconciliationJarSerializer",
"PaymentMethodSerializer",
]

View File

@@ -0,0 +1,15 @@
from rest_framework import serializers
from ..models.customers import Customer
class CustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = ["id", "name", "address", "email", "phone", "external_id"]
class ListCustomerSerializer(serializers.ModelSerializer):
class Meta:
model = Customer
fields = ["id", "name"]

View File

@@ -0,0 +1,31 @@
from rest_framework import serializers
from ..models.payments import ReconciliationJar, PaymentMethods
from .sales import SaleSerializer
class ReconciliationJarSerializer(serializers.ModelSerializer):
Sales = SaleSerializer(many=True, read_only=True)
class Meta:
model = ReconciliationJar
fields = [
"id",
"date_time",
"reconcilier",
"cash_taken",
"cash_discrepancy",
"total_cash_purchases",
"Sales",
]
class PaymentMethodSerializer(serializers.Serializer):
text = serializers.CharField()
value = serializers.CharField()
def to_representation(self, instance):
return {
"text": instance[1],
"value": instance[0],
}

View File

@@ -0,0 +1,23 @@
from rest_framework import serializers
from ..models.products import Product, ProductCategory
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = [
"id",
"name",
"active",
"price",
"measuring_unit",
"categories",
"external_id",
]
class ListProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ["id", "name"]

View File

@@ -0,0 +1,104 @@
from rest_framework import serializers
from ..models.sales import (
Sale,
SaleLine,
CatalogSale,
CatalogSaleLine,
Payment,
)
from .products import ListProductSerializer
from .customers import ListCustomerSerializer
class SaleLineSerializer(serializers.ModelSerializer):
class Meta:
model = SaleLine
fields = ["id", "sale", "product", "unit_price", "quantity"]
class SaleSerializer(serializers.ModelSerializer):
total = serializers.ReadOnlyField(source="get_total")
class Meta:
model = Sale
fields = [
"id",
"customer",
"date",
"saleline_set",
"total",
"payment_method",
"external_id",
]
class CatalogSaleLineSerializer(serializers.ModelSerializer):
class Meta:
model = CatalogSaleLine
read_only_fields = ["catalog_sale"]
fields = [
"id",
"catalog_sale",
"product",
"unit_price",
"quantity",
]
class CatalogSaleSerializer(serializers.ModelSerializer):
catalogsaleline_set = CatalogSaleLineSerializer(many=True, required=False)
total = serializers.ReadOnlyField(source="get_total")
class Meta:
model = CatalogSale
fields = [
"id",
"customer",
"date",
"catalogsaleline_set",
"total",
]
def create(self, validated_data):
lines_data = validated_data.pop("catalogsaleline_set", [])
catalog_sale = CatalogSale.objects.create(**validated_data)
for line_data in lines_data:
CatalogSaleLine.objects.create(catalog_sale=catalog_sale, **line_data)
return catalog_sale
class SummarySaleLineSerializer(serializers.ModelSerializer):
product = ListProductSerializer()
class Meta:
model = SaleLine
fields = ["product", "quantity", "unit_price", "description"]
class SaleSummarySerializer(serializers.ModelSerializer):
customer = ListCustomerSerializer()
lines = SummarySaleLineSerializer(many=True, source="saleline_set")
class Meta:
model = Sale
fields = ["id", "date", "customer", "payment_method", "lines"]
class SaleForRenconciliationSerializer(serializers.Serializer):
id = serializers.IntegerField()
date = serializers.DateTimeField()
payment_method = serializers.CharField()
customer = serializers.SerializerMethodField()
total = serializers.SerializerMethodField()
def get_customer(self, sale):
return {
"id": sale.customer.id,
"name": sale.customer.name,
}
def get_total(self, sale):
return sale.get_total()