Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ test2.csr

# Created by tests:
/media/

# venv
.venv/
5 changes: 5 additions & 0 deletions django_afip/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,11 @@ class PointOfSalesAdmin(admin.ModelAdmin):
)


@admin.register(models.ClientVatCondition)
class ClientVatConditionAdmin(admin.ModelAdmin):
search_fields = ("code", "description", "cmp_clase")
list_display = ("code", "description", "cmp_clase")

@admin.register(models.CurrencyType)
class CurrencyTypeAdmin(admin.ModelAdmin):
search_fields = (
Expand Down
65 changes: 65 additions & 0 deletions django_afip/fixtures/clientvatcondition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
- model: afip.clientvatcondition
fields:
code: 16
description: "Monotributo Trabajador Independiente Promovido"
cmp_clase: "A/M/C"

- model: afip.clientvatcondition
fields:
code: 15
description: "IVA No Alcanzado"
cmp_clase: "B/C"

- model: afip.clientvatcondition
fields:
code: 13
description: "Monotributista Social"
cmp_clase: "A/M/C"

- model: afip.clientvatcondition
fields:
code: 10
description: "IVA Liberado – Ley N° 19.640"
cmp_clase: "B/C"

- model: afip.clientvatcondition
fields:
code: 9
description: "Cliente del Exterior"
cmp_clase: "B/C"

- model: afip.clientvatcondition
fields:
code: 8
description: "Proveedor del Exterior"
cmp_clase: "B/C"

- model: afip.clientvatcondition
fields:
code: 7
description: "Sujeto No Categorizado"
cmp_clase: "B/C"

- model: afip.clientvatcondition
fields:
code: 6
description: "Responsable Monotributo"
cmp_clase: "A/M/C"

- model: afip.clientvatcondition
fields:
code: 5
description: "Consumidor Final"
cmp_clase: "B/C"

- model: afip.clientvatcondition
fields:
code: 4
description: "IVA Sujeto Exento"
cmp_clase: "B/C"

- model: afip.clientvatcondition
fields:
code: 1
description: "IVA Responsable Inscripto"
cmp_clase: "A/M/C"
42 changes: 42 additions & 0 deletions django_afip/management/commands/load_client_vat_conditions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from __future__ import annotations

from django.core.management.base import BaseCommand
from django.utils.translation import gettext as _

from django_afip import clients, models, serializers


class Command(BaseCommand):
help = _(
"Retrieves all the ClientVatConditions from the AFIP server and updates it in the DB."
)
requires_migrations_checks = True

def add_arguments(self, parser):
parser.add_argument(
"cuit",
type=int,
help=_("CUIT of the tax payer to be used to authenticate."),
)

def handle(self, *args, **options) -> None:
from django_afip.models import TaxPayer

tax_payer = TaxPayer.objects.get(cuit=options["cuit"])
ticket = tax_payer.get_or_create_ticket("wsfe")

client = clients.get_client("wsfe", sandbox=tax_payer.is_sandboxed)
response = client.service.FEParamGetCondicionIvaReceptor(
serializers.serialize_ticket(ticket),
)

for condition in response.ResultGet.CondicionIvaReceptor:
models.ClientVatCondition.objects.get_or_create(
code=condition.Id,
defaults={
"description": condition.Desc,
"cmp_clase": condition.Cmp_Clase,
},
)
self.stdout.write(self.style.SUCCESS(f"Loaded {condition.Desc}"))
self.stdout.write(self.style.SUCCESS("All done!"))
Comment on lines +1 to +42
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Esto debería ir en scripts/dump_metadata.py.

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Generated by Django 5.1.6 on 2025-02-26 14:09

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('afip', '0015_alter_taxpayer_logo'),
]

operations = [
migrations.CreateModel(
name='ClientVatCondition',
fields=[
('code', models.IntegerField(primary_key=True, serialize=False, verbose_name='code')),
('description', models.CharField(max_length=48, verbose_name='description')),
('cmp_clase', models.CharField(help_text='Receipt class this VAT condition applies to (A, B, C, or M).', max_length=5, verbose_name='cmp clase')),
],
options={
'verbose_name': 'Client VAT condition',
'verbose_name_plural': 'Client VAT conditions',
'unique_together': {('code', 'cmp_clase')},
},
),
migrations.AddField(
model_name='receipt',
name='client_vat_condition',
field=models.ForeignKey(help_text='The VAT condition of the recipient of this receipt. It should match the receipt type.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='receipts', to='afip.clientvatcondition', verbose_name='client vat condition'),
),
]
37 changes: 36 additions & 1 deletion django_afip/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@
def load_metadata() -> None:
"""Loads metadata from fixtures into the database."""

for model in GenericAfipType.SUBCLASSES:
for model in GenericAfipType.SUBCLASSES + [ClientVatCondition]:
label = model._meta.label.split(".")[1].lower()
management.call_command("loaddata", label, app="afip")

Expand Down Expand Up @@ -351,6 +351,30 @@ class Meta:
verbose_name_plural = _("optional types")


class ClientVatCondition(models.Model):
code = models.IntegerField(
_("code"),
primary_key=True,
)
description = models.CharField(
_("description"),
max_length=48,
)
cmp_clase = models.CharField(
_("cmp clase"),
max_length=5,
help_text=_("Receipt class this VAT condition applies to (A, B, C, or M)."),
)

def __str__(self) -> str:
return self.description

class Meta:
verbose_name = _("Client VAT condition")
verbose_name_plural = _("Client VAT conditions")
unique_together = ("code", "cmp_clase") # Ensure unique combination


class TaxPayer(models.Model):
"""Represents an AFIP TaxPayer.

Expand Down Expand Up @@ -1136,6 +1160,17 @@ class Receipt(models.Model):
help_text=_("The document type of the recipient of this receipt."),
on_delete=models.PROTECT,
)
client_vat_condition = models.ForeignKey(
ClientVatCondition,
verbose_name=_("client vat condition"),
help_text=_(
"The VAT condition of the recipient of this receipt. It should match the receipt type."
),
related_name="receipts",
on_delete=models.PROTECT,
null=True,
)

document_number = models.BigIntegerField(
_("document number"),
help_text=_("The document number of the recipient of this receipt."),
Expand Down
1 change: 1 addition & 0 deletions django_afip/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ def serialize_receipt(receipt: Receipt): # noqa: ANN201
ImpTrib=sum(tax.amount for tax in taxes),
MonId=receipt.currency.code,
MonCotiz=receipt.currency_quote,
CondicionIVAReceptorId=receipt.client_vat_condition.code,
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Crashes if receipt.client_vat_condition is None.

)
if int(receipt.concept.code) in (2, 3):
serialized.FchServDesde = serialize_date(receipt.service_start)
Expand Down