Coverage for invoices/serializers.py: 59%

108 statements  

« 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) 

16 

17 

18class FiscalRegimeSerializer(serializers.ModelSerializer): 

19 class Meta: 

20 model = FiscalRegime 

21 read_only_fields = ["random_slug", "description", "regime_key"] 

22 fields = read_only_fields + [] 

23 

24 

25class InvoiceUseSerializer(serializers.ModelSerializer): 

26 class Meta: 

27 model = InvoiceUse 

28 read_only_fields = ["random_slug", "description", "use_key"] 

29 fields = read_only_fields + [] 

30 

31 

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 ] 

48 

49 

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 ] 

63 

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 

68 

69 

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"] 

75 

76 

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"] 

82 

83 

84class InvoiceSerializer(serializers.ModelSerializer): 

85 invoice_configuration = InvoiceConfigurationSerializer() 

86 

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}} 

103 

104 def _make_facturapi_invoice(self, invoice, items): 

105 facturapi = FacturapiOrganizationClient(invoice.organization.facturapi_secret_key) 

106 

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 } 

115 

116 payment = invoice.product_charges.first().payment_allocations.first().payment 

117 

118 if isinstance(payment, ManualPayment): 

119 payment_form = payment.payment_method_type.type_key 

120 elif isinstance(payment, PaymentIntent): 

121 payment_form = "04" 

122 

123 return facturapi.Invoice.create(customer, items, payment_form) 

124 

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 

130 

131 invoice_configuration = validated_data.pop("invoice_configuration") 

132 

133 invoice_configuration = InvoiceConfiguration.objects.create(**invoice_configuration) 

134 

135 validated_data["invoice_configuration"] = invoice_configuration 

136 

137 invoice = super().create(validated_data) 

138 

139 items = [] 

140 

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."}) 

147 

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."}) 

151 

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 ) 

162 

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 ) 

192 

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) 

204 

205 return invoice 

206 

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 

214 

215 

216class InvoiceCancellationSerializer(serializers.ModelSerializer): 

217 """Serializer for invoice cancellation""" 

218 

219 class Meta: 

220 model = Invoice 

221 read_only_fields = ["random_slug", "status", "cancellation_status"] 

222 fields = read_only_fields + ["cancellation_motive"] 

223 

224 

225class InvoiceExportSerializer(serializers.ModelSerializer): 

226 """ 

227 Serializer for InvoiceExport 

228 """ 

229 

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"]