Coverage for invoices/serializers.py: 59%
108 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-06-07 12:46 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-06-07 12:46 -0600
1from rest_framework import serializers
2from app.facturapi import FacturapiOrganizationClient
3from memberships.models import Membership
4from memberships.serializers import BasicMembershipSerializer
5from payments.models import PaymentIntent, ManualPayment
6from .models import (
7 FiscalRegime,
8 InvoiceUse,
9 ProductInvoiceConfiguration,
10 MembershipInvoiceConfiguration,
11 InvoiceConfiguration,
12 InvoiceProductConfiguration,
13 Invoice,
14 InvoiceExport,
15)
18class FiscalRegimeSerializer(serializers.ModelSerializer):
19 class Meta:
20 model = FiscalRegime
21 read_only_fields = ["random_slug", "description", "regime_key"]
22 fields = read_only_fields + []
25class InvoiceUseSerializer(serializers.ModelSerializer):
26 class Meta:
27 model = InvoiceUse
28 read_only_fields = ["random_slug", "description", "use_key"]
29 fields = read_only_fields + []
32class ProductInvoiceConfigurationSerializer(serializers.ModelSerializer):
33 class Meta:
34 model = ProductInvoiceConfiguration
35 read_only_fields = ["random_slug"]
36 fields = read_only_fields + [
37 "product",
38 "uses",
39 "description",
40 "product_key",
41 "unit_key",
42 "for_membership",
43 "for_event",
44 "for_stock",
45 "tax_included",
46 "taxability",
47 ]
50class MembershipInvoiceConfigurationSerializer(serializers.ModelSerializer):
51 class Meta:
52 model = MembershipInvoiceConfiguration
53 read_only_fields = ["random_slug"]
54 fields = read_only_fields + [
55 "membership",
56 "tax_system",
57 "legal_name",
58 "email",
59 "tax_id",
60 "zip_code",
61 "default",
62 ]
64 def to_representation(self, instance):
65 representation = super(MembershipInvoiceConfigurationSerializer, self).to_representation(instance)
66 representation["tax_system"] = instance.tax_system.regime_key
67 return representation
70class InvoiceConfigurationSerializer(serializers.ModelSerializer):
71 class Meta:
72 model = InvoiceConfiguration
73 read_only_fields = ["random_slug"]
74 fields = read_only_fields + ["tax_system", "legal_name", "email", "tax_id", "zip_code"]
77class InvoiceProductConfigurationSerializer(serializers.ModelSerializer):
78 class Meta:
79 model = InvoiceProductConfiguration
80 read_only_fields = ["random_slug"]
81 fields = read_only_fields + ["description", "product_charge", "product_key", "unit_key", "amount"]
84class InvoiceSerializer(serializers.ModelSerializer):
85 invoice_configuration = InvoiceConfigurationSerializer()
87 class Meta:
88 model = Invoice
89 read_only_fields = [
90 "random_slug",
91 "pdf",
92 "xml",
93 "sat_uuid",
94 "folio_number",
95 "status",
96 "cancellation_status",
97 "organization",
98 "created_at",
99 "total_amount",
100 ]
101 fields = read_only_fields + ["membership", "product_charges", "invoice_configuration", "use"]
102 extra_kwargs = {"membership": {"required": False}}
104 def _make_facturapi_invoice(self, invoice, items):
105 facturapi = FacturapiOrganizationClient(invoice.organization.facturapi_secret_key)
107 customer = {
108 "legal_name": invoice.invoice_configuration.legal_name,
109 "tax_id": invoice.invoice_configuration.tax_id,
110 "tax_system": invoice.invoice_configuration.tax_system,
111 "address": {
112 "zip": invoice.invoice_configuration.zip_code,
113 },
114 }
116 payment = invoice.product_charges.first().payment_allocations.first().payment
118 if isinstance(payment, ManualPayment):
119 payment_form = payment.payment_method_type.type_key
120 elif isinstance(payment, PaymentIntent):
121 payment_form = "04"
123 return facturapi.Invoice.create(customer, items, payment_form)
125 def create(self, validated_data):
126 if "organization" not in validated_data:
127 validated_data["organization"] = validated_data.get("product_charges")[0].membership.organization
128 if "membership" not in validated_data:
129 validated_data["membership"] = validated_data.get("product_charges")[0].membership
131 invoice_configuration = validated_data.pop("invoice_configuration")
133 invoice_configuration = InvoiceConfiguration.objects.create(**invoice_configuration)
135 validated_data["invoice_configuration"] = invoice_configuration
137 invoice = super().create(validated_data)
139 items = []
141 for product_charge in invoice.product_charges.all():
142 try:
143 product_configuration = product_charge.product.invoice_configuration
144 except Exception:
145 invoice.delete()
146 raise serializers.ValidationError({"detail": "El producto no esta configurado para ser facturado."})
148 if (product_charge.paid_amount - product_charge.invoiced_amount) == 0:
149 invoice.delete()
150 raise serializers.ValidationError({"detail": "El producto no tiene monto a facturar."})
152 InvoiceProductConfiguration.objects.create(
153 invoice=invoice,
154 product_charge=product_charge,
155 description=product_configuration.description,
156 product_key=product_configuration.product_key,
157 unit_key=product_configuration.unit_key,
158 amount=product_charge.paid_amount - product_charge.invoiced_amount,
159 tax_included=product_configuration.tax_included,
160 taxability=product_configuration.taxability,
161 )
163 if product_configuration.tax_included:
164 items.append(
165 {
166 "quantity": product_charge.quantity,
167 "product": {
168 "description": product_configuration.description,
169 "product_key": product_configuration.product_key,
170 "price": float(product_charge.paid_amount - product_charge.invoiced_amount),
171 "unit_key": product_configuration.unit_key,
172 "tax_included": product_configuration.tax_included,
173 "taxability": product_configuration.taxability,
174 },
175 }
176 )
177 else:
178 items.append(
179 {
180 "quantity": product_charge.quantity,
181 "product": {
182 "description": product_configuration.description,
183 "product_key": product_configuration.product_key,
184 "price": float(product_charge.paid_amount - product_charge.invoiced_amount),
185 "unit_key": product_configuration.unit_key,
186 "tax_included": product_configuration.tax_included,
187 "taxability": product_configuration.taxability,
188 "taxes": [],
189 },
190 }
191 )
193 try:
194 facturapi_invoice = self._make_facturapi_invoice(invoice, items)
195 invoice.facturapi_invoice_id = facturapi_invoice.get("id")
196 invoice.sat_uuid = facturapi_invoice.get("uuid")
197 invoice.folio_number = facturapi_invoice.get("folio_number")
198 invoice.status = Invoice.Status.VALID
199 invoice.save()
200 invoice.save_files()
201 invoice.send_invoice_email()
202 except Exception as e:
203 raise serializers.ValidationError(e)
205 return invoice
207 def to_representation(self, instance):
208 representation = super(InvoiceSerializer, self).to_representation(instance)
209 membership_slug = representation.get("membership")
210 membership = Membership.objects.get(random_slug=membership_slug)
211 serializer = BasicMembershipSerializer(instance=membership)
212 representation["membership"] = serializer.data
213 return representation
216class InvoiceCancellationSerializer(serializers.ModelSerializer):
217 """Serializer for invoice cancellation"""
219 class Meta:
220 model = Invoice
221 read_only_fields = ["random_slug", "status", "cancellation_status"]
222 fields = read_only_fields + ["cancellation_motive"]
225class InvoiceExportSerializer(serializers.ModelSerializer):
226 """
227 Serializer for InvoiceExport
228 """
230 class Meta:
231 model = InvoiceExport
232 read_only_fields = ["random_slug", "created_at", "export_file"]
233 fields = read_only_fields + ["from_date", "to_date", "status"]