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.
- wbaccounting/admin/__init__.py +1 -1
- wbaccounting/admin/entry_accounting_information.py +15 -1
- wbaccounting/factories/entry_accounting_information.py +21 -3
- wbaccounting/generators/base.py +14 -5
- wbaccounting/migrations/0013_vat_alter_entryaccountinginformation_vat_vatrate.py +84 -0
- wbaccounting/models/__init__.py +1 -1
- wbaccounting/models/entry_accounting_information.py +72 -4
- wbaccounting/models/model_tasks.py +4 -2
- wbaccounting/serializers/__init__.py +1 -0
- wbaccounting/serializers/entry_accounting_information.py +9 -5
- wbaccounting/tests/conftest.py +2 -0
- wbaccounting/tests/test_models/test_entry_accounting_information.py +24 -0
- wbaccounting/tests/test_serializers/test_entry_accounting_information.py +4 -2
- wbaccounting/urls.py +2 -0
- wbaccounting/viewsets/entry_accounting_information.py +10 -3
- {wbaccounting-1.60.1.dist-info → wbaccounting-1.61.0.dist-info}/METADATA +1 -1
- {wbaccounting-1.60.1.dist-info → wbaccounting-1.61.0.dist-info}/RECORD +18 -17
- {wbaccounting-1.60.1.dist-info → wbaccounting-1.61.0.dist-info}/WHEEL +0 -0
wbaccounting/admin/__init__.py
CHANGED
|
@@ -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
|
|
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 =
|
|
33
|
+
vat = factory.SubFactory(VatFactory)
|
|
16
34
|
send_mail = factory.Faker("pybool")
|
|
17
35
|
counterparty_is_private = False
|
|
18
36
|
|
wbaccounting/generators/base.py
CHANGED
|
@@ -51,7 +51,9 @@ class AbstractBookingEntryGenerator(abc.ABC):
|
|
|
51
51
|
|
|
52
52
|
@staticmethod
|
|
53
53
|
@abc.abstractmethod
|
|
54
|
-
def generate_booking_entries(
|
|
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],
|
|
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(
|
|
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=
|
|
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
|
+
]
|
wbaccounting/models/__init__.py
CHANGED
|
@@ -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.
|
|
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(
|
|
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
|
-
|
|
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",
|
wbaccounting/tests/conftest.py
CHANGED
|
@@ -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,
|
|
@@ -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=
|
|
6
|
-
wbaccounting/admin/__init__.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
101
|
-
wbaccounting-1.
|
|
102
|
-
wbaccounting-1.
|
|
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,,
|
|
File without changes
|