wbaccounting 1.60.1__py2.py3-none-any.whl → 1.61.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  from .booking_entry import BookingEntryInline, BookingEntryModelAdmin
2
- from .entry_accounting_information import EntryAccountingInformationModelAdmin
2
+ from .entry_accounting_information import EntryAccountingInformationModelAdmin, VatModelAdmin
3
3
  from .invoice_type import InvoiceTypeModelAdmin
4
4
  from .invoice import InvoiceModelAdmin
5
5
  from .transactions import TransactionModelAdmin
@@ -1,6 +1,20 @@
1
1
  from django.contrib import admin
2
2
 
3
- from wbaccounting.models import EntryAccountingInformation
3
+ from wbaccounting.models import EntryAccountingInformation, Vat, VatRate
4
+
5
+
6
+ class VarRateInline(admin.TabularInline):
7
+ model = VatRate
8
+ fk_name = "vat"
9
+ fields = ("rate", "timespan")
10
+
11
+
12
+ @admin.register(Vat)
13
+ class VatModelAdmin(admin.ModelAdmin):
14
+ list_display = ("name",)
15
+ search_fields = ["name"]
16
+
17
+ inlines = [VarRateInline]
4
18
 
5
19
 
6
20
  @admin.register(EntryAccountingInformation)
@@ -1,8 +1,26 @@
1
+ from datetime import date
2
+
1
3
  import factory
2
- from factory.fuzzy import FuzzyDecimal
4
+ from psycopg.types.range import DateRange
3
5
  from wbcore.contrib.directory.factories import CompanyFactory, EmailContactFactory
4
6
 
5
- from wbaccounting.models import EntryAccountingInformation
7
+ from wbaccounting.models import EntryAccountingInformation, Vat, VatRate
8
+
9
+
10
+ class VatFactory(factory.django.DjangoModelFactory):
11
+ name = "Default VAT"
12
+ primary = True
13
+
14
+ class Meta:
15
+ model = Vat
16
+ django_get_or_create = ("primary",)
17
+
18
+ @factory.post_generation
19
+ def initiate_rate(self, create, extracted, **kwargs):
20
+ if not create:
21
+ return
22
+ if not self.rates.exists():
23
+ VatRate.objects.create(vat=self, timespan=DateRange(date(2010, 1, 1), None), rate=0.08)
6
24
 
7
25
 
8
26
  class EntryAccountingInformationFactory(factory.django.DjangoModelFactory):
@@ -12,7 +30,7 @@ class EntryAccountingInformationFactory(factory.django.DjangoModelFactory):
12
30
 
13
31
  entry = factory.SubFactory("wbcore.contrib.directory.factories.EntryFactory")
14
32
  tax_id = factory.Faker("text", max_nb_chars=64)
15
- vat = FuzzyDecimal(0, 0.9, 4)
33
+ vat = factory.SubFactory(VatFactory)
16
34
  send_mail = factory.Faker("pybool")
17
35
  counterparty_is_private = False
18
36
 
@@ -51,7 +51,9 @@ class AbstractBookingEntryGenerator(abc.ABC):
51
51
 
52
52
  @staticmethod
53
53
  @abc.abstractmethod
54
- def generate_booking_entries(from_date: date, to_date: date, counterparty: Entry) -> Iterable[BookingEntry]:
54
+ def generate_booking_entries(
55
+ from_date: date, to_date: date, counterparty: Entry, booking_date: date
56
+ ) -> Iterable[BookingEntry]:
55
57
  """
56
58
  Generates a sequence of booking entries for a specified date range and counterparty.
57
59
 
@@ -62,6 +64,7 @@ class AbstractBookingEntryGenerator(abc.ABC):
62
64
  from_date (date): The start date of the period for which booking entries are to be generated.
63
65
  to_date (date): The end date of the period for which booking entries are to be generated.
64
66
  counterparty (Entry): The counterparty associated with the booking entries to be generated.
67
+ booking_date (date): The booking entry date
65
68
 
66
69
  Returns:
67
70
  Iterable[BookingEntry]: A sequence of BookingEntry instances generated for the specified
@@ -97,9 +100,13 @@ class AbstractBookingEntryGenerator(abc.ABC):
97
100
 
98
101
 
99
102
  def generate_booking_entries(
100
- _class: type[AbstractBookingEntryGenerator], from_date: date, to_date: date, counterparty: Entry
103
+ _class: type[AbstractBookingEntryGenerator],
104
+ from_date: date,
105
+ to_date: date,
106
+ counterparty: Entry,
107
+ booking_date: date,
101
108
  ):
102
- booking_entries = _class.generate_booking_entries(from_date, to_date, counterparty)
109
+ booking_entries = _class.generate_booking_entries(from_date, to_date, counterparty, booking_date)
103
110
  for booking_entry in booking_entries:
104
111
  booking_entry.save()
105
112
 
@@ -108,11 +115,13 @@ class TestGenerator(AbstractBookingEntryGenerator):
108
115
  TITLE = "Test Generator"
109
116
 
110
117
  @staticmethod
111
- def generate_booking_entries(from_date: date, to_date: date, counterparty: Entry) -> Iterable[BookingEntry]:
118
+ def generate_booking_entries(
119
+ from_date: date, to_date: date, counterparty: Entry, booking_date: date
120
+ ) -> Iterable[BookingEntry]:
112
121
  yield BookingEntry(
113
122
  title="Test Booking Entry",
114
123
  counterparty=counterparty,
115
- booking_date=date.today(),
124
+ booking_date=booking_date,
116
125
  reference_date=date.today(),
117
126
  net_value=100,
118
127
  currency=Currency.objects.first(),
@@ -0,0 +1,84 @@
1
+ # Generated by Django 5.2.9 on 2026-01-21 12:55
2
+ from datetime import date
3
+
4
+ import django.contrib.postgres.constraints
5
+ import django.contrib.postgres.fields.ranges
6
+ import django.db.models.deletion
7
+ from django.db import migrations, models
8
+ from psycopg.types.range import DateRange
9
+
10
+ def migrate_vat(apps, schema_editor):
11
+ EntryAccountingInformation = apps.get_model('wbaccounting', 'EntryAccountingInformation')
12
+ Vat = apps.get_model('wbaccounting', 'Vat')
13
+ VatRate = apps.get_model('wbaccounting', 'VatRate')
14
+ objs = []
15
+ default_vat = Vat.objects.create(name=str(0.08), primary=True)
16
+ VatRate.objects.create(rate=0.08, vat=default_vat, timespan=DateRange(date(2010, 1, 1), None))
17
+ for entry in EntryAccountingInformation.objects.all():
18
+ if entry.old_vat:
19
+ try:
20
+ rate = VatRate.objects.get(rate=entry.old_vat)
21
+ entry.vat = rate.vat
22
+ except VatRate.DoesNotExist:
23
+ vat = Vat.objects.create(name=str(entry.old_vat))
24
+ rate = VatRate.objects.create(rate=entry.old_vat, vat=vat, timespan=DateRange(date(2010,1,1), None))
25
+ entry.vat = rate.vat
26
+ else:
27
+ entry.vat = default_vat
28
+ objs.append(entry)
29
+ EntryAccountingInformation.objects.bulk_update(objs, ["vat"])
30
+
31
+ class Migration(migrations.Migration):
32
+
33
+ dependencies = [
34
+ ('wbaccounting', '0012_entryaccountinginformation_external_invoice_users'),
35
+ ]
36
+
37
+ operations = [
38
+ migrations.CreateModel(
39
+ name='Vat',
40
+ fields=[
41
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
42
+ ('name', models.CharField(max_length=255)),
43
+ ("primary", models.BooleanField(default=False, verbose_name='Primary'))
44
+ ],
45
+ ),
46
+ migrations.RenameField(model_name='entryaccountinginformation', old_name="vat", new_name="old_vat",),
47
+ migrations.AddField(
48
+ model_name='entryaccountinginformation',
49
+ name='vat',
50
+ field=models.ForeignKey(default=None, blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vat_recipients', to='wbaccounting.vat', verbose_name='VAT'),
51
+ preserve_default=False,
52
+ ),
53
+ migrations.CreateModel(
54
+ name='VatRate',
55
+ fields=[
56
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
57
+ ('timespan', django.contrib.postgres.fields.ranges.DateRangeField(verbose_name='Timespan')),
58
+ ('rate', models.DecimalField(decimal_places=4, max_digits=5)),
59
+ ('vat', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rates', to='wbaccounting.vat')),
60
+ ],
61
+ options={
62
+ 'verbose_name': 'Vat Rate',
63
+ 'verbose_name_plural': 'Vat Rates',
64
+ 'constraints': [django.contrib.postgres.constraints.ExclusionConstraint(expressions=[('timespan', '&&'), ('vat', '=')], name='exclude_overlapping_vat_rates')],
65
+ },
66
+ ),
67
+ migrations.RunPython(migrate_vat),
68
+ migrations.RemoveField(model_name='entryaccountinginformation', name="old_vat"),
69
+ migrations.AlterField(
70
+ model_name='entryaccountinginformation',
71
+ name='vat',
72
+ field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT,
73
+ related_name='vat_recipients', to='wbaccounting.vat', verbose_name='VAT'),
74
+ preserve_default=False,
75
+ ),
76
+ migrations.AlterModelOptions(
77
+ name='vat',
78
+ options={'verbose_name': 'Vat Group', 'verbose_name_plural': 'Vat Groups'},
79
+ ),
80
+ migrations.AddConstraint(
81
+ model_name='vat',
82
+ constraint=models.UniqueConstraint(fields=('primary',), name='unique_primary_vat_group'),
83
+ ),
84
+ ]
@@ -1,6 +1,6 @@
1
1
  from .booking_entry import BookingEntry
2
2
  from .invoice import Invoice
3
3
  from .invoice_type import InvoiceType
4
- from .entry_accounting_information import EntryAccountingInformation
4
+ from .entry_accounting_information import EntryAccountingInformation, Vat, VatRate
5
5
  from .transactions import Transaction
6
6
  from .model_tasks import submit_invoices_as_task
@@ -1,8 +1,11 @@
1
1
  from contextlib import suppress
2
2
  from datetime import date
3
+ from decimal import Decimal
3
4
 
5
+ from django.contrib.postgres.constraints import ExclusionConstraint
6
+ from django.contrib.postgres.fields import DateRangeField, RangeOperators
4
7
  from django.db import models
5
- from django.db.models import Q, QuerySet
8
+ from django.db.models import Q, QuerySet, UniqueConstraint
6
9
  from django.db.models.signals import post_save
7
10
  from django.dispatch import receiver
8
11
  from django.utils.module_loading import import_string
@@ -10,6 +13,7 @@ from dynamic_preferences.registries import global_preferences_registry as gpr
10
13
  from wbcore.contrib.authentication.models import User
11
14
  from wbcore.contrib.currency.models import Currency
12
15
  from wbcore.signals import pre_merge
16
+ from wbcore.utils.models import PrimaryMixin
13
17
 
14
18
  from wbaccounting.models.model_tasks import generate_booking_entries_as_task
15
19
 
@@ -47,6 +51,63 @@ class EntryAccountingInformationDefaultQuerySet(QuerySet):
47
51
  return self.filter(Q(counterparty_is_private=False) | Q(exempt_users=user))
48
52
 
49
53
 
54
+ class Vat(PrimaryMixin, models.Model):
55
+ name = models.CharField(max_length=255)
56
+
57
+ def get_rate(self, date: date) -> Decimal:
58
+ try:
59
+ return self.rates.get(timespan__contains=date).rate
60
+ except VatRate.DoesNotExist:
61
+ return Decimal("0.0")
62
+
63
+ def __str__(self) -> str:
64
+ return self.name
65
+
66
+ class Meta:
67
+ verbose_name = "Vat Group"
68
+ verbose_name_plural = "Vat Groups"
69
+ constraints = [
70
+ UniqueConstraint(
71
+ name="unique_primary_vat_group",
72
+ fields=["primary"],
73
+ )
74
+ ]
75
+
76
+ @classmethod
77
+ def get_representation_endpoint(cls):
78
+ return "wbaccounting:vatrepresentation-list"
79
+
80
+ @classmethod
81
+ def get_representation_value_key(cls):
82
+ return "id"
83
+
84
+ @classmethod
85
+ def get_representation_label_key(cls):
86
+ return "{{name}}"
87
+
88
+
89
+ class VatRate(models.Model):
90
+ vat = models.ForeignKey("wbaccounting.Vat", related_name="rates", on_delete=models.CASCADE)
91
+ timespan = DateRangeField(verbose_name="Timespan")
92
+ rate = models.DecimalField(decimal_places=4, max_digits=5)
93
+
94
+ class Meta:
95
+ verbose_name = "Vat Rate"
96
+ verbose_name_plural = "Vat Rates"
97
+ constraints = [
98
+ ExclusionConstraint(
99
+ name="exclude_overlapping_vat_rates",
100
+ expressions=[
101
+ ("timespan", RangeOperators.OVERLAPS),
102
+ ("vat", RangeOperators.EQUAL),
103
+ ],
104
+ ),
105
+ ]
106
+
107
+ def __str__(self) -> str:
108
+ return f"{self.vat}: {self.rate}"
109
+
110
+
50
111
  class EntryAccountingInformation(models.Model):
51
112
  # Link to Entry
52
113
  entry = models.OneToOneField(
@@ -58,7 +119,9 @@ class EntryAccountingInformation(models.Model):
58
119
 
59
120
  # Tax Information
60
121
  tax_id = models.CharField(max_length=512, blank=True, null=True, verbose_name="Tax ID")
61
- vat = models.FloatField(blank=True, null=True, verbose_name="VAT")
122
+ vat = models.ForeignKey(
123
+ "wbaccounting.Vat", related_name="vat_recipients", on_delete=models.PROTECT, verbose_name="VAT"
124
+ )
62
125
 
63
126
  # Invoice Information
64
127
  default_currency = models.ForeignKey(
@@ -123,6 +186,11 @@ class EntryAccountingInformation(models.Model):
123
186
 
124
187
  objects = EntryAccountingInformationDefaultQuerySet.as_manager()
125
188
 
189
+ def save(self, *args, **kwargs):
190
+ if not self.vat:
191
+ self.vat = Vat.objects.get_or_create(primary=True, defaults={"name": "Default VAT group"})[0]
192
+ super().save(*args, **kwargs)
193
+
126
194
  class Meta:
127
195
  verbose_name = "Counterparty"
128
196
  verbose_name_plural = "Counterparties"
@@ -146,9 +214,9 @@ class EntryAccountingInformation(models.Model):
146
214
  def get_representation_label_key(cls):
147
215
  return "{{entry_repr}}"
148
216
 
149
- def generate_booking_entries(self, from_date: date, to_date: date):
217
+ def generate_booking_entries(self, from_date: date, to_date: date, booking_date: date):
150
218
  generate_booking_entries_as_task.delay( # type: ignore
151
- self.booking_entry_generator or "", from_date, to_date, self.entry.id
219
+ self.booking_entry_generator or "", from_date, to_date, self.entry.id, booking_date
152
220
  )
153
221
 
154
222
 
@@ -66,8 +66,10 @@ def refresh_invoice_document_as_task(invoice_id):
66
66
 
67
67
 
68
68
  @shared_task(queue=Queue.DEFAULT.value)
69
- def generate_booking_entries_as_task(func: str, from_date: date, to_date: date, counterparty_id: int):
69
+ def generate_booking_entries_as_task(
70
+ func: str, from_date: date, to_date: date, counterparty_id: int, booking_date: date
71
+ ):
70
72
  with suppress(ImportError):
71
73
  generator = import_string(func)
72
74
  counterparty = Entry.objects.get(id=counterparty_id)
73
- generate_booking_entries(generator, from_date, to_date, counterparty)
75
+ generate_booking_entries(generator, from_date, to_date, counterparty, booking_date)
@@ -2,6 +2,7 @@ from .invoice_type import InvoiceTypeModelSerializer, InvoiceTypeRepresentationS
2
2
  from .entry_accounting_information import (
3
3
  EntryAccountingInformationModelSerializer,
4
4
  EntryAccountingInformationRepresentationSerializer,
5
+ VatRepresentationSerializer,
5
6
  )
6
7
  from .invoice import InvoiceRepresentationSerializer, InvoiceModelSerializer
7
8
  from .booking_entry import (
@@ -1,5 +1,3 @@
1
- from decimal import Decimal
2
-
3
1
  from django.db.models import Q
4
2
  from django.dispatch import receiver
5
3
  from rest_framework.exceptions import ValidationError
@@ -18,10 +16,16 @@ from wbcore.contrib.directory.serializers import (
18
16
  from wbcore.signals import add_instance_additional_resource
19
17
 
20
18
  from wbaccounting.generators.base import get_all_booking_entry_choices
21
- from wbaccounting.models import EntryAccountingInformation
19
+ from wbaccounting.models import EntryAccountingInformation, Vat
22
20
  from wbaccounting.serializers import InvoiceTypeRepresentationSerializer
23
21
 
24
22
 
23
+ class VatRepresentationSerializer(serializers.RepresentationSerializer):
24
+ class Meta:
25
+ model = Vat
26
+ fields = ("id", "name", "primary")
27
+
28
+
25
29
  class EntryAccountingInformationRepresentationSerializer(serializers.RepresentationSerializer):
26
30
  entry_repr = serializers.CharField(source="entry.computed_str", read_only=True)
27
31
 
@@ -37,7 +41,7 @@ class EntryAccountingInformationModelSerializer(serializers.ModelSerializer):
37
41
  _entry = EntryRepresentationSerializer(source="entry")
38
42
  _default_currency = CurrencyRepresentationSerializer(source="default_currency")
39
43
  _default_invoice_type = InvoiceTypeRepresentationSerializer(source="default_invoice_type")
40
- vat = serializers.DecimalField(percent=True, required=False, max_digits=6, decimal_places=4, default=Decimal(0))
44
+ _vat = VatRepresentationSerializer(source="vat")
41
45
  _exempt_users = UserRepresentationSerializer(source="exempt_users", many=True)
42
46
 
43
47
  _email_to = EmailContactRepresentationSerializer(source="email_to", many=True, ignore_filter=True)
@@ -90,13 +94,13 @@ class EntryAccountingInformationModelSerializer(serializers.ModelSerializer):
90
94
  class Meta:
91
95
  model = EntryAccountingInformation
92
96
 
93
- percent_fields = ["vat"]
94
97
  fields = (
95
98
  "id",
96
99
  "entry",
97
100
  "_entry",
98
101
  "tax_id",
99
102
  "vat",
103
+ "_vat",
100
104
  "default_currency",
101
105
  "_default_currency",
102
106
  "default_invoice_type",
@@ -13,6 +13,7 @@ from wbaccounting.factories import (
13
13
  LocalCurrencyTransactionFactory,
14
14
  TransactionFactory,
15
15
  )
16
+ from wbaccounting.factories.entry_accounting_information import VatFactory
16
17
  from wbcore.contrib.authentication.factories import (
17
18
  SuperUserFactory,
18
19
  UserActivityFactory,
@@ -27,6 +28,7 @@ from wbcore.contrib.directory.factories import (
27
28
  from wbcore.contrib.geography.tests.signals import app_pre_migration
28
29
  from wbcore.tests.conftest import *
29
30
 
31
+ register(VatFactory)
30
32
  register(BookingEntryFactory)
31
33
  register(InvoiceFactory)
32
34
  register(InvoiceTypeFactory)
@@ -1,15 +1,39 @@
1
+ from datetime import date
2
+ from decimal import Decimal
3
+
1
4
  import pytest
2
5
  from dynamic_preferences.registries import global_preferences_registry
6
+ from psycopg.types.range import DateRange
3
7
  from wbcore.contrib.authentication.models import User
4
8
  from wbcore.contrib.currency.models import Currency
5
9
 
6
10
  from wbaccounting.models import BookingEntry, EntryAccountingInformation
7
11
  from wbaccounting.models.entry_accounting_information import (
12
+ VatRate,
8
13
  default_currency,
9
14
  default_email_body,
10
15
  )
11
16
 
12
17
 
18
+ @pytest.mark.django_db
19
+ class TestVat:
20
+ def test_get_rate(self, vat):
21
+ current_rate = vat.rates.first()
22
+ assert vat.get_rate(date(2009, 12, 31)) == Decimal("0.0") # test exclusion
23
+ assert vat.get_rate(date(2010, 1, 1)) == Decimal("0.08") # test default factory data
24
+ assert vat.get_rate(date(2050, 1, 1)) == Decimal("0.08") # test default unbounded
25
+
26
+ # change the end date of the existing rate and create a new rate
27
+ new_pivot = date(2011, 1, 1)
28
+ current_rate.timespan = DateRange(current_rate.timespan.lower, new_pivot)
29
+ current_rate.save()
30
+ VatRate.objects.create(vat=vat, rate=0.1, timespan=DateRange(new_pivot, date(2012, 1, 1)))
31
+ assert vat.get_rate(date(2011, 1, 1)) == Decimal("0.1")
32
+ assert vat.get_rate(date(2012, 1, 1)) == Decimal("0") # excluded so default to the previous rate
33
+
34
+ assert vat.get_rate(date(2050, 1, 1)) == Decimal("0")
35
+
36
+
13
37
  @pytest.mark.django_db
14
38
  class TestEntryAccountingInformation:
15
39
  def test_str(self, entry_accounting_information: EntryAccountingInformation):
@@ -23,6 +23,7 @@ class TestEntryAccountingInformationModelSerializer:
23
23
  "_entry",
24
24
  "tax_id",
25
25
  "vat",
26
+ "_vat",
26
27
  "default_currency",
27
28
  "_default_currency",
28
29
  "default_invoice_type",
@@ -45,12 +46,13 @@ class TestEntryAccountingInformationModelSerializer:
45
46
  "_additional_resources",
46
47
  ) == tuple(serializer.data.keys()) # type: ignore
47
48
 
48
- def test_deserialize(self, entry_accounting_information_factory, entry, currency):
49
+ def test_deserialize(self, entry_accounting_information_factory, entry, currency, vat):
49
50
  data = factory.build(dict, FACTORY_CLASS=entry_accounting_information_factory)
50
51
  data["entry"] = entry.pk
52
+ data["vat"] = vat.id
51
53
  data["default_currency"] = currency.pk
52
54
  serializer = EntryAccountingInformationModelSerializer(data=data)
53
- assert serializer.is_valid()
55
+ assert serializer.is_valid(raise_exception=True)
54
56
 
55
57
 
56
58
  @pytest.mark.django_db
wbaccounting/urls.py CHANGED
@@ -16,6 +16,7 @@ from wbaccounting.viewsets import (
16
16
  TransactionModelViewSet,
17
17
  TransactionRepresentationViewSet,
18
18
  )
19
+ from wbaccounting.viewsets.entry_accounting_information import VatRepresentationViewSet
19
20
 
20
21
  router = WBCoreRouter()
21
22
  router.register(r"bookingentry", BookingEntryModelViewSet, basename="bookingentry")
@@ -41,6 +42,7 @@ router.register(r"futurecashflow", FutureCashFlowPandasAPIViewSet, basename="fut
41
42
  router.register(
42
43
  r"futurecashflowtransaction", FutureCashFlowTransactionsPandasAPIViewSet, basename="futurecashflowtransaction"
43
44
  )
45
+ router.register(r"vatrepresentation", VatRepresentationViewSet, basename="vatrepresentation")
44
46
 
45
47
  entry_router = WBCoreRouter()
46
48
 
@@ -8,11 +8,12 @@ from rest_framework.response import Response
8
8
  from wbcore import viewsets
9
9
  from wbcore.utils.date import get_date_interval_from_request
10
10
 
11
- from wbaccounting.models import EntryAccountingInformation, Invoice
11
+ from wbaccounting.models import EntryAccountingInformation, Invoice, Vat
12
12
  from wbaccounting.models.booking_entry import BookingEntry
13
13
  from wbaccounting.serializers import (
14
14
  EntryAccountingInformationModelSerializer,
15
15
  EntryAccountingInformationRepresentationSerializer,
16
+ VatRepresentationSerializer,
16
17
  )
17
18
  from wbaccounting.viewsets.buttons import EntryAccountingInformationButtonConfig
18
19
  from wbaccounting.viewsets.display import EntryAccountingInformationDisplayConfig
@@ -21,6 +22,12 @@ from wbaccounting.viewsets.titles import EntryAccountingInformationTitleConfig
21
22
  from ..permissions import CanGenerateBookingEntry, CanGenerateInvoice
22
23
 
23
24
 
25
+ class VatRepresentationViewSet(viewsets.RepresentationViewSet):
26
+ search_fields = ("name",)
27
+ queryset = Vat.objects.all()
28
+ serializer_class = VatRepresentationSerializer
29
+
30
+
24
31
  class EntryAccountingInformationRepresentationViewSet(viewsets.RepresentationViewSet):
25
32
  search_fields = ("entry__computed_str",)
26
33
  queryset = EntryAccountingInformation.objects.all()
@@ -74,7 +81,7 @@ class EntryAccountingInformationModelViewSet(viewsets.ModelViewSet):
74
81
  eai = get_object_or_404(EntryAccountingInformation, id=pk)
75
82
  start, end = get_date_interval_from_request(request, request_type="POST")
76
83
  if start and end:
77
- eai.generate_booking_entries(start, end) # type: ignore
84
+ eai.generate_booking_entries(start, end, date.today()) # type: ignore
78
85
  return Response(
79
86
  {"__notification": {"title": "Booking Entries are being generated in the background."}},
80
87
  status=status.HTTP_200_OK,
@@ -90,7 +97,7 @@ class EntryAccountingInformationModelViewSet(viewsets.ModelViewSet):
90
97
  if counterparties := request.POST.get("counterparties", ""):
91
98
  eais = EntryAccountingInformation.objects.filter(entry__id__in=counterparties.split(","))
92
99
  for eai in eais:
93
- eai.generate_booking_entries(start, end) # type: ignore
100
+ eai.generate_booking_entries(start, end, date.today()) # type: ignore
94
101
  return Response(
95
102
  {"__notification": {"title": "Booking Entries are being generated in the background."}},
96
103
  status=status.HTTP_200_OK,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wbaccounting
3
- Version: 1.60.1
3
+ Version: 1.61.0
4
4
  Summary: A workbench module for managing invoicing and simple accounting.
5
5
  Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
6
6
  Requires-Dist: wbcore
@@ -2,23 +2,23 @@ wbaccounting/__init__.py,sha256=J-j-u0itpEFT6irdmWmixQqYMadNl1X91TxUmoiLHMI,22
2
2
  wbaccounting/apps.py,sha256=g0CDsvZoOQFiCiWg9v-DJv99oPpm4LTQYunOZ5n2gTM,99
3
3
  wbaccounting/dynamic_preferences_registry.py,sha256=yT-fTQ_v_BCYhpZO-ARUdZ2XMQVIIyNORKRY3l3Fba0,3356
4
4
  wbaccounting/permissions.py,sha256=LZclqe3nBYQiK-SyjvmShGbxZ-2LORBxA3BZVyUjbFk,556
5
- wbaccounting/urls.py,sha256=KVhdDApmfMNMKZZN-ykIQyJSGXfaUjn5xg8dA8xyWFQ,3001
6
- wbaccounting/admin/__init__.py,sha256=klh0BoNdyTz54qRp1a1PSP0u7G4LbgQE_m3C68Z-ObE,284
5
+ wbaccounting/urls.py,sha256=tgIZrX1tiaxxYDcu0Dy-iyCb4cYh-iVLyDyTAvcmyjw,3183
6
+ wbaccounting/admin/__init__.py,sha256=D5piSNbUQqEbFnynCxmCLMJ3xQPQgQoIIjHy_s-p8_Q,299
7
7
  wbaccounting/admin/booking_entry.py,sha256=PrJWFFgmJrHbfcfIx0451WAcnD9yjqDFbyKjweQB-jM,1891
8
- wbaccounting/admin/entry_accounting_information.py,sha256=uob78cxN9jlinssHG2kqG0GlYcdGDReFdDh58OkElBc,424
8
+ wbaccounting/admin/entry_accounting_information.py,sha256=5p6QhE_rT4-4bxIfKAqXsf2_eX92-791m9Ux52vVsY8,707
9
9
  wbaccounting/admin/invoice.py,sha256=LhZDttPyfYnrMh0Bv68MRCrI_vD2vXb_3cYG0fDawH0,763
10
10
  wbaccounting/admin/invoice_type.py,sha256=84YgJQArbbGH5Rl9aS6ncJYYjZ9BHR81rI-IkjsI-uQ,245
11
11
  wbaccounting/admin/transactions.py,sha256=vyx-xuQ5cf_5kW5apuRkvTQTFcsPMbw_verFOigTc7M,424
12
12
  wbaccounting/factories/__init__.py,sha256=3F29_EXN8nmuI-sbhpc8gzTksdQ9QRnLfyGw8Hfua4M,309
13
13
  wbaccounting/factories/booking_entry.py,sha256=sQYxZY4B0DK3--Xn01loJ2Y1vwusZ31RCyeu0JvHen0,928
14
- wbaccounting/factories/entry_accounting_information.py,sha256=04DrOtHqOsepKF92jXq6CLpGJEYEFUoKQfHnoV-KRSM,1769
14
+ wbaccounting/factories/entry_accounting_information.py,sha256=8yM_X2fAt0jLR21N3YBPlN89bSKQg6kr07FhRfHdd-8,2266
15
15
  wbaccounting/factories/invoice.py,sha256=K1kmT1PzSpC6pkMbfBD0pLfUN4nxZ6UY_NMX9avVYnc,1718
16
16
  wbaccounting/factories/transactions.py,sha256=hesASwyi3k60BRAPrVsI_20ulEllQE_eWi6GCGGeE-0,1272
17
17
  wbaccounting/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  wbaccounting/files/invoice_document_file.py,sha256=--ohJhUhhr7l7hvHsTbtu4fQ3mZh7tXZXkXK2gWCaic,4259
19
19
  wbaccounting/files/utils.py,sha256=3Ae8_BJGBpDhzgWKOLl6XefkP_ghxUeBp83TaN5Hl-M,14032
20
20
  wbaccounting/generators/__init__.py,sha256=HMzN5HbicO03Ji5YabzVUScI7UWkC-VfdwaWeEvtplg,138
21
- wbaccounting/generators/base.py,sha256=OEL98wVUhYniblo1xZQEpRWpV271iKTC05OyzTypcho,4606
21
+ wbaccounting/generators/base.py,sha256=bqOBmxnjL-ET5ykv06eZyW6sbdcZm2FQjXSjLVJcqo8,4782
22
22
  wbaccounting/io/handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  wbaccounting/io/handlers/transactions.py,sha256=wu_25akE-J-LKvs-1-erjtON2ffnkhsX0grTF5vfa6o,1415
24
24
  wbaccounting/io/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -32,43 +32,44 @@ wbaccounting/migrations/0009_invoicetype_alter_bookingentry_options_and_more.py,
32
32
  wbaccounting/migrations/0010_alter_bookingentry_options.py,sha256=MubJcJ864jphINUO3EbDf5IJ9D-HqTRqA_3PsNf9HKg,564
33
33
  wbaccounting/migrations/0011_transaction.py,sha256=CAFDQiZFIP-xJ__GqCyvsmMUazgMUGw_cR7H40GWxz0,4085
34
34
  wbaccounting/migrations/0012_entryaccountinginformation_external_invoice_users.py,sha256=4uGToqhfM5VGMgHNnBgvASjoVTox1sYvjx0RY_mgbDA,815
35
+ wbaccounting/migrations/0013_vat_alter_entryaccountinginformation_vat_vatrate.py,sha256=q9PWbSrw-Ax6gDH-RA_5KRvQFcDFfEaNn-VmEoXMs1E,3986
35
36
  wbaccounting/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
- wbaccounting/models/__init__.py,sha256=qk2F9CHovzTR-opHJpPpp9HbblLX95izFNfL0FnQCts,263
37
+ wbaccounting/models/__init__.py,sha256=ingIhODVA1HUjLyJ4FFKq59eTUISRT_CpoLiGdn0cUs,277
37
38
  wbaccounting/models/booking_entry.py,sha256=7apQyTySBU-vILPyMIOJlNCuPLYy4jgMnneJehOrHpk,6927
38
- wbaccounting/models/entry_accounting_information.py,sha256=jU-WVhxjBPtOVQhcenmrPUdQtSipZO12AHqU9ACH-vs,6291
39
+ wbaccounting/models/entry_accounting_information.py,sha256=y7IR0nslmuf_WCHw7qN3Mt-_ChdR6gRXvUdeMaaopFw,8415
39
40
  wbaccounting/models/invoice.py,sha256=ILaUC3lWG6oM4GpNpEVuuEbiijYk5a5fRv0BsnW8NFE,19258
40
41
  wbaccounting/models/invoice_type.py,sha256=SMYKJGyL7rm4_7Sb6yaLT88ZIqXDmh9LA2KpyELsDBg,855
41
- wbaccounting/models/model_tasks.py,sha256=V0p8xXORYSf98mbZLb6y9mcgkrpKiFqF8afXlNEtw9s,2361
42
+ wbaccounting/models/model_tasks.py,sha256=RINNcaUhwfGzkxiBIvYPiAWdPOeF_yIQvRVCsovdgCo,2401
42
43
  wbaccounting/models/transactions.py,sha256=V6tOJjLSJJVsqynT53KABqHJGtp22f6Gl2kQy9ok53o,3875
43
44
  wbaccounting/processors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
45
  wbaccounting/processors/dummy_processor.py,sha256=eHeH6IeUlPHcN1vggzmtVcffH0xgYFqAmA4CycVOK2I,158
45
- wbaccounting/serializers/__init__.py,sha256=GPDCuRvKe8k203tq_6hxzWDqmq_WoHTEolstkbBDmi0,576
46
+ wbaccounting/serializers/__init__.py,sha256=fQp5ASMccf19xsc93dKUCLhVQIu3s2ye0SSjoUAU7XI,609
46
47
  wbaccounting/serializers/booking_entry.py,sha256=7LI1_CaOxHftIgJXlpy-8Y9S6HXQaUXRW4oVAXNHBsM,2724
47
48
  wbaccounting/serializers/consolidated_invoice.py,sha256=GA-AuSSMdPcNrIfj3L7wMaCF7YIxzDNAmbVkPOGtaJk,4347
48
- wbaccounting/serializers/entry_accounting_information.py,sha256=rv6k-NLr7Wuu8o4kz8lMmt1rCisz5u73k_YOuks9eFs,6046
49
+ wbaccounting/serializers/entry_accounting_information.py,sha256=_8ByykqyhoKnoeiNwbahWOr3safXfoXdmItO3hNSX_8,6099
49
50
  wbaccounting/serializers/invoice.py,sha256=WuE9GgqCn7ujODdrziDe10CQegdb6UAVKKanz80XjYs,3582
50
51
  wbaccounting/serializers/invoice_type.py,sha256=a1aeN2ecYkEusXZBORUZ18duZsmbUokQ_h19r32In78,490
51
52
  wbaccounting/serializers/transactions.py,sha256=AYThwp8dlKP4WiVHnfyq47GC2D2-pXO41nuJwa7LhEw,1976
52
53
  wbaccounting/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
- wbaccounting/tests/conftest.py,sha256=wKMgjTqQNvkbgZVf_RtNhVEf8Y1XXUZetjVqBI-M2uA,1874
54
+ wbaccounting/tests/conftest.py,sha256=o79VgxMVTQmtOFa_Nsf6QJKgGHUCuel95kiCT2UaDLQ,1970
54
55
  wbaccounting/tests/test_processors.py,sha256=_ux2EJJ6XDTfZg9_UACrjAIEXVDHVwao6ydBzliBYi4,761
55
56
  wbaccounting/tests/test_displays/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
57
  wbaccounting/tests/test_displays/test_booking_entries.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
58
  wbaccounting/tests/test_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
58
59
  wbaccounting/tests/test_models/test_booking_entries.py,sha256=VXGXpaIrm232qBv6YoEZf0UBnZjLseoJESiMBYZXihw,6509
59
- wbaccounting/tests/test_models/test_entry_accounting_information.py,sha256=yiRJdgDAQ8Ur3H73aJJQkAmHocjt41-KVh0BfQbbpEM,4475
60
+ wbaccounting/tests/test_models/test_entry_accounting_information.py,sha256=9J390VZAxxNP-HaVY6Lqoz1yZfHXKWbELj3nz5CFS1Y,5522
60
61
  wbaccounting/tests/test_models/test_invoice_types.py,sha256=5QYZIEl5pZDfDH6frsAtsvWjtNqOtRSkvhgA7VZz_00,700
61
62
  wbaccounting/tests/test_models/test_invoices.py,sha256=KkjED-eVWDtRoqY34EiAQWEk57xPCOsgoTliBghMhI0,3560
62
63
  wbaccounting/tests/test_models/test_transactions.py,sha256=PbyLk4-0Vp8OuMGA7ZlzNLobCf1XKe47ZscSziIOds0,1720
63
64
  wbaccounting/tests/test_serializers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
65
  wbaccounting/tests/test_serializers/test_booking_entries.py,sha256=sjaYy0yHB4H4KeioTjVvqS36_CkcatQ5k8brOwh3Ayg,2548
65
- wbaccounting/tests/test_serializers/test_entry_accounting_information.py,sha256=j8T5xHEpxyx-nxIzZ2jzd-QL2M25JTIERKzggWf8M7A,2352
66
+ wbaccounting/tests/test_serializers/test_entry_accounting_information.py,sha256=kuPnlWIzWkxcP65gUG2I0vxFicfYTO0fS-ERIu-YGz8,2426
66
67
  wbaccounting/tests/test_serializers/test_invoice_types.py,sha256=B38fLqfcD6k93VcxeNPBnvfGS07nUjRqbDJDx1NHRcQ,1215
67
68
  wbaccounting/tests/test_serializers/test_transactions.py,sha256=FW2MpsQEmLb2SHPBKh89uQPPGJhe6IuMvBmtUv6h3Q4,3455
68
69
  wbaccounting/viewsets/__init__.py,sha256=up2PNbfvbIwpcL8EuefLvldf6hqr2D86MtCKFNH1w04,608
69
70
  wbaccounting/viewsets/booking_entry.py,sha256=BsckKn7f3SN5x56LJar5gCsGYSflXWCv9ALDoQFjk2E,2220
70
71
  wbaccounting/viewsets/cashflows.py,sha256=wylKbeaSyFTYx81plR-9WcLrrVV-THB4KDItrL-u2l0,4777
71
- wbaccounting/viewsets/entry_accounting_information.py,sha256=pbRHkv0iSm1kV7Ym5mYgvkRxtb8d_t8PA3HnOt58Hks,6099
72
+ wbaccounting/viewsets/entry_accounting_information.py,sha256=v1_dZ3jO3uEuH6lFdKLHzzZ9eORhBpVJaQZNMw5tang,6345
72
73
  wbaccounting/viewsets/invoice.py,sha256=rYbbuuhDM1eULp82vKeP8aWY9TMVLZCA6Pc1CyFAVmA,11657
73
74
  wbaccounting/viewsets/invoice_type.py,sha256=-Go0bbN5fXPebr-5xvgwGM6P2ULfuqif2hdnY81GY-E,1000
74
75
  wbaccounting/viewsets/transactions.py,sha256=d1kfdDzF7VAg7RbZhWvyh9REJzlHIO9gkRmlfuXB04A,1390
@@ -97,6 +98,6 @@ wbaccounting/viewsets/titles/booking_entry.py,sha256=SNQJOYXf3QcjTtL3Co2uIYZC69d
97
98
  wbaccounting/viewsets/titles/entry_accounting_information.py,sha256=76kXkK5ij-HV29tt8efUuvplQ9d1CrQklT8sSv6zHYE,344
98
99
  wbaccounting/viewsets/titles/invoice.py,sha256=mr-iufJpM2SYuvexZ3XPLjBmQgPatgIMn1JC4eHLdnY,566
99
100
  wbaccounting/viewsets/titles/invoice_type.py,sha256=szgA0iSYVILjLvhFhuLcq_WhMWv04_ESEVvogMYOkyk,301
100
- wbaccounting-1.60.1.dist-info/METADATA,sha256=Lkwyc-9U2bNQXdj9s7QgRQjRtc5EV9Shj73EUItu40Q,217
101
- wbaccounting-1.60.1.dist-info/WHEEL,sha256=aha0VrrYvgDJ3Xxl3db_g_MDIW-ZexDdrc_m-Hk8YY4,105
102
- wbaccounting-1.60.1.dist-info/RECORD,,
101
+ wbaccounting-1.61.0.dist-info/METADATA,sha256=CsG0jxQHSj05kB-7WzuRgghDfM1VDiYZIPu_los6XZo,217
102
+ wbaccounting-1.61.0.dist-info/WHEEL,sha256=aha0VrrYvgDJ3Xxl3db_g_MDIW-ZexDdrc_m-Hk8YY4,105
103
+ wbaccounting-1.61.0.dist-info/RECORD,,