wbaccounting 2.2.1__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.
Files changed (102) hide show
  1. wbaccounting/__init__.py +1 -0
  2. wbaccounting/admin/__init__.py +5 -0
  3. wbaccounting/admin/booking_entry.py +53 -0
  4. wbaccounting/admin/entry_accounting_information.py +10 -0
  5. wbaccounting/admin/invoice.py +26 -0
  6. wbaccounting/admin/invoice_type.py +8 -0
  7. wbaccounting/admin/transactions.py +16 -0
  8. wbaccounting/apps.py +5 -0
  9. wbaccounting/dynamic_preferences_registry.py +107 -0
  10. wbaccounting/factories/__init__.py +10 -0
  11. wbaccounting/factories/booking_entry.py +21 -0
  12. wbaccounting/factories/entry_accounting_information.py +46 -0
  13. wbaccounting/factories/invoice.py +43 -0
  14. wbaccounting/factories/transactions.py +32 -0
  15. wbaccounting/files/__init__.py +0 -0
  16. wbaccounting/files/invoice_document_file.py +134 -0
  17. wbaccounting/files/utils.py +331 -0
  18. wbaccounting/generators/__init__.py +6 -0
  19. wbaccounting/generators/base.py +120 -0
  20. wbaccounting/io/handlers/__init__.py +0 -0
  21. wbaccounting/io/handlers/transactions.py +32 -0
  22. wbaccounting/io/parsers/__init__.py +0 -0
  23. wbaccounting/io/parsers/societe_generale_lux.py +49 -0
  24. wbaccounting/io/parsers/societe_generale_lux_prenotification.py +60 -0
  25. wbaccounting/migrations/0001_initial_squashed_squashed_0005_alter_bookingentry_counterparty_and_more.py +284 -0
  26. wbaccounting/migrations/0006_alter_invoice_status.py +30 -0
  27. wbaccounting/migrations/0007_alter_invoice_options.py +23 -0
  28. wbaccounting/migrations/0008_alter_invoice_options.py +20 -0
  29. wbaccounting/migrations/0009_invoicetype_alter_bookingentry_options_and_more.py +366 -0
  30. wbaccounting/migrations/0010_alter_bookingentry_options.py +20 -0
  31. wbaccounting/migrations/0011_transaction.py +103 -0
  32. wbaccounting/migrations/0012_entryaccountinginformation_external_invoice_users.py +25 -0
  33. wbaccounting/migrations/__init__.py +0 -0
  34. wbaccounting/models/__init__.py +6 -0
  35. wbaccounting/models/booking_entry.py +167 -0
  36. wbaccounting/models/entry_accounting_information.py +157 -0
  37. wbaccounting/models/invoice.py +467 -0
  38. wbaccounting/models/invoice_type.py +30 -0
  39. wbaccounting/models/model_tasks.py +71 -0
  40. wbaccounting/models/transactions.py +112 -0
  41. wbaccounting/permissions.py +6 -0
  42. wbaccounting/processors/__init__.py +0 -0
  43. wbaccounting/processors/dummy_processor.py +5 -0
  44. wbaccounting/serializers/__init__.py +12 -0
  45. wbaccounting/serializers/booking_entry.py +78 -0
  46. wbaccounting/serializers/consolidated_invoice.py +109 -0
  47. wbaccounting/serializers/entry_accounting_information.py +149 -0
  48. wbaccounting/serializers/invoice.py +95 -0
  49. wbaccounting/serializers/invoice_type.py +16 -0
  50. wbaccounting/serializers/transactions.py +50 -0
  51. wbaccounting/tests/__init__.py +0 -0
  52. wbaccounting/tests/conftest.py +65 -0
  53. wbaccounting/tests/test_displays/__init__.py +0 -0
  54. wbaccounting/tests/test_displays/test_booking_entries.py +1 -0
  55. wbaccounting/tests/test_models/__init__.py +0 -0
  56. wbaccounting/tests/test_models/test_booking_entries.py +119 -0
  57. wbaccounting/tests/test_models/test_entry_accounting_information.py +81 -0
  58. wbaccounting/tests/test_models/test_invoice_types.py +21 -0
  59. wbaccounting/tests/test_models/test_invoices.py +73 -0
  60. wbaccounting/tests/test_models/test_transactions.py +40 -0
  61. wbaccounting/tests/test_processors.py +28 -0
  62. wbaccounting/tests/test_serializers/__init__.py +0 -0
  63. wbaccounting/tests/test_serializers/test_booking_entries.py +69 -0
  64. wbaccounting/tests/test_serializers/test_entry_accounting_information.py +64 -0
  65. wbaccounting/tests/test_serializers/test_invoice_types.py +35 -0
  66. wbaccounting/tests/test_serializers/test_transactions.py +72 -0
  67. wbaccounting/urls.py +68 -0
  68. wbaccounting/viewsets/__init__.py +12 -0
  69. wbaccounting/viewsets/booking_entry.py +61 -0
  70. wbaccounting/viewsets/buttons/__init__.py +3 -0
  71. wbaccounting/viewsets/buttons/booking_entry.py +15 -0
  72. wbaccounting/viewsets/buttons/entry_accounting_information.py +100 -0
  73. wbaccounting/viewsets/buttons/invoice.py +65 -0
  74. wbaccounting/viewsets/cashflows.py +124 -0
  75. wbaccounting/viewsets/display/__init__.py +8 -0
  76. wbaccounting/viewsets/display/booking_entry.py +58 -0
  77. wbaccounting/viewsets/display/cashflows.py +58 -0
  78. wbaccounting/viewsets/display/entry_accounting_information.py +91 -0
  79. wbaccounting/viewsets/display/invoice.py +218 -0
  80. wbaccounting/viewsets/display/invoice_type.py +19 -0
  81. wbaccounting/viewsets/display/transactions.py +35 -0
  82. wbaccounting/viewsets/endpoints/__init__.py +1 -0
  83. wbaccounting/viewsets/endpoints/invoice.py +6 -0
  84. wbaccounting/viewsets/entry_accounting_information.py +143 -0
  85. wbaccounting/viewsets/invoice.py +277 -0
  86. wbaccounting/viewsets/invoice_type.py +25 -0
  87. wbaccounting/viewsets/menu/__init__.py +6 -0
  88. wbaccounting/viewsets/menu/booking_entry.py +15 -0
  89. wbaccounting/viewsets/menu/cashflows.py +10 -0
  90. wbaccounting/viewsets/menu/entry_accounting_information.py +11 -0
  91. wbaccounting/viewsets/menu/invoice.py +15 -0
  92. wbaccounting/viewsets/menu/invoice_type.py +15 -0
  93. wbaccounting/viewsets/menu/transactions.py +15 -0
  94. wbaccounting/viewsets/titles/__init__.py +4 -0
  95. wbaccounting/viewsets/titles/booking_entry.py +12 -0
  96. wbaccounting/viewsets/titles/entry_accounting_information.py +12 -0
  97. wbaccounting/viewsets/titles/invoice.py +23 -0
  98. wbaccounting/viewsets/titles/invoice_type.py +12 -0
  99. wbaccounting/viewsets/transactions.py +34 -0
  100. wbaccounting-2.2.1.dist-info/METADATA +8 -0
  101. wbaccounting-2.2.1.dist-info/RECORD +102 -0
  102. wbaccounting-2.2.1.dist-info/WHEEL +5 -0
@@ -0,0 +1,91 @@
1
+ from typing import Optional
2
+
3
+ from wbcore.metadata.configs import display as dp
4
+ from wbcore.metadata.configs.display.instance_display.shortcuts import Display, default
5
+ from wbcore.metadata.configs.display.instance_display.utils import repeat_field
6
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
7
+
8
+
9
+ class EntryAccountingInformationDisplayConfig(DisplayViewConfig):
10
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
11
+ return dp.ListDisplay(
12
+ fields=[
13
+ dp.Field(key="entry", label="CRM", width=400),
14
+ dp.Field(key="tax_id", label="Tax ID"),
15
+ dp.Field(key="vat", label="VAT", width=100),
16
+ dp.Field(key="default_currency", label="Currency", width=100),
17
+ dp.Field(key="send_mail", label="Send Mail", width=100),
18
+ ],
19
+ formatting=[
20
+ dp.Formatting(
21
+ column="counterparty_is_private",
22
+ formatting_rules=[dp.FormattingRule(condition=("==", True), style={"backgroundColor": "#708090"})],
23
+ )
24
+ ],
25
+ legends=[
26
+ dp.Legend(
27
+ key="counterparty_is_private",
28
+ items=[dp.LegendItem(icon="#708090", label="Private Counterparty", value=True)],
29
+ )
30
+ ],
31
+ )
32
+
33
+ def get_instance_display(self) -> Display:
34
+ return dp.Display(
35
+ pages=[
36
+ dp.Page(
37
+ title="General Information",
38
+ layouts={
39
+ default(): dp.Layout(
40
+ grid_template_areas=[
41
+ ["entry", "booking_entry_generator"],
42
+ ["counterparty_is_private", "exempt_users"],
43
+ ["default_currency", "tax_id"],
44
+ ["vat", "default_invoice_type"],
45
+ ["external_invoice_users", "."],
46
+ ],
47
+ )
48
+ },
49
+ ),
50
+ dp.Page(
51
+ title="E-Mail Settings",
52
+ layouts={
53
+ dp.default(): dp.Layout(
54
+ grid_template_areas=[
55
+ ["send_mail", "."],
56
+ ["email_to", "."],
57
+ ["email_cc", "."],
58
+ ["email_bcc", "."],
59
+ ["email_subject", "."],
60
+ [repeat_field(2, "email_body")],
61
+ ],
62
+ grid_template_columns=["600px", "1fr"],
63
+ )
64
+ },
65
+ ),
66
+ dp.Page(
67
+ title="Invoices",
68
+ layouts={
69
+ default(): dp.Layout(
70
+ grid_template_areas=[
71
+ ["invoices"],
72
+ ],
73
+ grid_template_rows=["600px"],
74
+ inlines=[dp.Inline(key="invoices", endpoint="invoices")],
75
+ )
76
+ },
77
+ ),
78
+ dp.Page(
79
+ title="Bookings",
80
+ layouts={
81
+ default(): dp.Layout(
82
+ grid_template_areas=[
83
+ ["bookingentries"],
84
+ ],
85
+ grid_template_rows=["600px"],
86
+ inlines=[dp.Inline(key="bookingentries", endpoint="bookingentries")],
87
+ )
88
+ },
89
+ ),
90
+ ]
91
+ )
@@ -0,0 +1,218 @@
1
+ from typing import Optional
2
+
3
+ from rest_framework.reverse import reverse
4
+ from wbaccounting.models import Invoice
5
+ from wbcore.contrib.color.enums import WBColor
6
+ from wbcore.metadata.configs import display as dp
7
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
8
+ Display,
9
+ create_simple_display,
10
+ create_simple_section,
11
+ )
12
+ from wbcore.metadata.configs.display.instance_display.utils import repeat_field
13
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
14
+
15
+ COLOR_DRAFT = "rgb(255,249,196)"
16
+ COLOR_SUBMITTED = "rgb(51, 204, 255)"
17
+ COLOR_SENT = "rgb(255, 153, 102)"
18
+ COLOR_PAID = "rgb(214, 229, 145)"
19
+
20
+
21
+ class InvoiceDisplayConfig(DisplayViewConfig):
22
+ def get_list_display(self) -> Optional[dp.ListDisplay]:
23
+ return dp.ListDisplay(
24
+ fields=[
25
+ dp.Field(key="status", label="Status"),
26
+ dp.Field(key="type", label="Type"),
27
+ dp.Field(key="counterparty", label="Counterparty"),
28
+ dp.Field(key="title", label="Title"),
29
+ dp.Field(key="invoice_date", label="Invoice Date"),
30
+ dp.Field(key="reference_date", label="Reference Date"),
31
+ dp.Field(key="gross_value", label="Gross Value"),
32
+ dp.Field(key="net_value", label="Net Value"),
33
+ ],
34
+ legends=[
35
+ dp.Legend(
36
+ key="status",
37
+ items=[
38
+ dp.LegendItem(
39
+ icon=COLOR_DRAFT,
40
+ label=Invoice.Status.DRAFT.label,
41
+ value=Invoice.Status.DRAFT.name,
42
+ ),
43
+ dp.LegendItem(
44
+ icon=COLOR_SUBMITTED,
45
+ label=Invoice.Status.SUBMITTED.label,
46
+ value=Invoice.Status.SUBMITTED.name,
47
+ ),
48
+ dp.LegendItem(
49
+ icon=WBColor.BLUE_LIGHT.value,
50
+ label=Invoice.Status.APPROVED.label,
51
+ value=Invoice.Status.APPROVED.name,
52
+ ),
53
+ dp.LegendItem(
54
+ icon=COLOR_SENT,
55
+ label=Invoice.Status.SENT.label,
56
+ value=Invoice.Status.SENT.name,
57
+ ),
58
+ dp.LegendItem(
59
+ icon=WBColor.GREEN_LIGHT.value,
60
+ label=Invoice.Status.PAID.label,
61
+ value=Invoice.Status.PAID.name,
62
+ ),
63
+ dp.LegendItem(
64
+ icon=WBColor.GREY.value,
65
+ label=Invoice.Status.CANCELLED.label,
66
+ value=Invoice.Status.CANCELLED.name,
67
+ ),
68
+ ],
69
+ )
70
+ ],
71
+ formatting=[
72
+ dp.Formatting(
73
+ column="status",
74
+ formatting_rules=[
75
+ dp.FormattingRule(
76
+ style={"backgroundColor": COLOR_DRAFT},
77
+ condition=("==", Invoice.Status.DRAFT.name),
78
+ ),
79
+ dp.FormattingRule(
80
+ style={"backgroundColor": COLOR_SUBMITTED},
81
+ condition=("==", Invoice.Status.SUBMITTED.name),
82
+ ),
83
+ dp.FormattingRule(
84
+ style={"backgroundColor": WBColor.BLUE_LIGHT.value},
85
+ condition=("==", Invoice.Status.APPROVED.name),
86
+ ),
87
+ dp.FormattingRule(
88
+ style={"backgroundColor": COLOR_SENT},
89
+ condition=("==", Invoice.Status.SENT.name),
90
+ ),
91
+ dp.FormattingRule(
92
+ style={"backgroundColor": WBColor.GREEN_LIGHT.value},
93
+ condition=("==", Invoice.Status.PAID.name),
94
+ ),
95
+ dp.FormattingRule(
96
+ style={"backgroundColor": WBColor.GREY.value},
97
+ condition=("==", Invoice.Status.CANCELLED.name),
98
+ ),
99
+ ],
100
+ ),
101
+ ],
102
+ )
103
+
104
+ def get_instance_display(self) -> Display:
105
+ return create_simple_display(
106
+ [
107
+ [repeat_field(2, "status")],
108
+ ["title", "invoice_type"],
109
+ ["counterparty", "reference_date"],
110
+ ["invoice_date", "invoice_currency"],
111
+ ["text_above", "text_below"],
112
+ [repeat_field(2, "booking_entry_section")],
113
+ ],
114
+ [
115
+ create_simple_section(
116
+ "booking_entry_section", "Booking Entries", [["bookingentries"]], "bookingentries", collapsed=True
117
+ )
118
+ ],
119
+ )
120
+
121
+
122
+ class ConsolidatedInvoiceDisplayConfig(DisplayViewConfig):
123
+ def get_list_display(self) -> dp.ListDisplay:
124
+ return dp.ListDisplay(
125
+ fields=[
126
+ dp.Field(key="value", label="Value"),
127
+ ],
128
+ tree=True,
129
+ tree_group_pinned="left",
130
+ tree_group_field="group",
131
+ tree_group_label="Group",
132
+ tree_group_level_options=[
133
+ dp.TreeGroupLevelOption(
134
+ filter_key="lookup",
135
+ list_endpoint=reverse(
136
+ "wbaccounting:consolidated-invoice-list",
137
+ args=[],
138
+ request=self.request,
139
+ ),
140
+ )
141
+ ],
142
+ legends=[
143
+ dp.Legend(
144
+ key="status",
145
+ label="Lowest Status",
146
+ items=[
147
+ dp.LegendItem(
148
+ icon=COLOR_DRAFT,
149
+ label=Invoice.Status.DRAFT.label,
150
+ value=Invoice.Status.DRAFT.name,
151
+ ),
152
+ dp.LegendItem(
153
+ icon=COLOR_SUBMITTED,
154
+ label=Invoice.Status.SUBMITTED.label,
155
+ value=Invoice.Status.SUBMITTED.name,
156
+ ),
157
+ dp.LegendItem(
158
+ icon=WBColor.BLUE_LIGHT.value,
159
+ label=Invoice.Status.APPROVED.label,
160
+ value=Invoice.Status.APPROVED.name,
161
+ ),
162
+ dp.LegendItem(
163
+ icon=COLOR_SENT,
164
+ label=Invoice.Status.SENT.label,
165
+ value=Invoice.Status.SENT.name,
166
+ ),
167
+ dp.LegendItem(
168
+ icon=WBColor.GREEN_LIGHT.value,
169
+ label=Invoice.Status.PAID.label,
170
+ value=Invoice.Status.PAID.name,
171
+ ),
172
+ dp.LegendItem(
173
+ icon=WBColor.GREY.value,
174
+ label=Invoice.Status.CANCELLED.label,
175
+ value=Invoice.Status.CANCELLED.name,
176
+ ),
177
+ ],
178
+ ),
179
+ ],
180
+ formatting=[
181
+ dp.Formatting(
182
+ column="num_paid",
183
+ formatting_rules=[
184
+ dp.FormattingRule(
185
+ style={"backgroundColor": COLOR_PAID},
186
+ condition=(">", 0),
187
+ )
188
+ ],
189
+ ),
190
+ dp.Formatting(
191
+ column="num_sent",
192
+ formatting_rules=[
193
+ dp.FormattingRule(
194
+ style={"backgroundColor": COLOR_SENT},
195
+ condition=(">", 0),
196
+ )
197
+ ],
198
+ ),
199
+ dp.Formatting(
200
+ column="num_submitted",
201
+ formatting_rules=[
202
+ dp.FormattingRule(
203
+ style={"backgroundColor": COLOR_SUBMITTED},
204
+ condition=(">", 0),
205
+ )
206
+ ],
207
+ ),
208
+ dp.Formatting(
209
+ column="num_draft",
210
+ formatting_rules=[
211
+ dp.FormattingRule(
212
+ style={"backgroundColor": COLOR_DRAFT},
213
+ condition=(">", 0),
214
+ )
215
+ ],
216
+ ),
217
+ ],
218
+ )
@@ -0,0 +1,19 @@
1
+ from wbcore.metadata.configs import display as dp
2
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
3
+ Display,
4
+ create_simple_display,
5
+ )
6
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
7
+
8
+
9
+ class InvoiceTypeDisplayConfig(DisplayViewConfig):
10
+ def get_list_display(self) -> dp.ListDisplay:
11
+ return dp.ListDisplay(
12
+ fields=[
13
+ dp.Field(key="name", label="Name"),
14
+ dp.Field(key="processor", label="Processor"),
15
+ ]
16
+ )
17
+
18
+ def get_instance_display(self) -> Display:
19
+ return create_simple_display([["name", "processor"]])
@@ -0,0 +1,35 @@
1
+ from django.utils.translation import gettext as _
2
+ from wbcore.metadata.configs import display as dp
3
+ from wbcore.metadata.configs.display.instance_display.shortcuts import (
4
+ Display,
5
+ create_simple_display,
6
+ )
7
+ from wbcore.metadata.configs.display.view_config import DisplayViewConfig
8
+
9
+
10
+ class TransactionDisplayConfig(DisplayViewConfig):
11
+ def get_list_display(self) -> dp.ListDisplay:
12
+ return dp.ListDisplay(
13
+ fields=[
14
+ dp.Field(key="booking_date", label=_("Booking Date"), width=150),
15
+ dp.Field(key="value_date", label=_("Value Date"), width=150),
16
+ dp.Field(key="bank_account", label=_("Bank Account"), width=450),
17
+ # dp.Field(key="from_bank_account", label=_("Source"), width=300),
18
+ # dp.Field(key="to_bank_account", label=_("Target"), width=300),
19
+ dp.Field(key="value_local_ccy", label=_("Value (Local)"), width=150),
20
+ dp.Field(key="value", label=_("Value"), width=150),
21
+ dp.Field(key="description", label=_("Description"), width=450),
22
+ ]
23
+ )
24
+
25
+ def get_instance_display(self) -> Display:
26
+ return create_simple_display(
27
+ [
28
+ ["booking_date", "value_date"],
29
+ ["bank_account", "."],
30
+ ["from_bank_account", "to_bank_account"],
31
+ ["value", "currency"],
32
+ ["value_local_ccy", "fx_rate"],
33
+ ["description", "description"],
34
+ ]
35
+ )
@@ -0,0 +1 @@
1
+ from .invoice import ConsolidatedInvoiceEndpointConfig
@@ -0,0 +1,6 @@
1
+ from wbcore.metadata.configs.endpoints import EndpointViewConfig
2
+
3
+
4
+ class ConsolidatedInvoiceEndpointConfig(EndpointViewConfig):
5
+ def _get_instance_endpoint(self, **kwargs):
6
+ return "{{casted_endpoint}}"
@@ -0,0 +1,143 @@
1
+ from datetime import date
2
+
3
+ from django.db.models import Exists, OuterRef, QuerySet
4
+ from rest_framework import status
5
+ from rest_framework.decorators import action
6
+ from rest_framework.permissions import IsAuthenticated
7
+ from rest_framework.response import Response
8
+ from wbaccounting.models import EntryAccountingInformation, Invoice
9
+ from wbaccounting.models.booking_entry import BookingEntry
10
+ from wbaccounting.serializers import (
11
+ EntryAccountingInformationModelSerializer,
12
+ EntryAccountingInformationRepresentationSerializer,
13
+ )
14
+ from wbaccounting.viewsets.buttons import EntryAccountingInformationButtonConfig
15
+ from wbaccounting.viewsets.display import EntryAccountingInformationDisplayConfig
16
+ from wbaccounting.viewsets.titles import EntryAccountingInformationTitleConfig
17
+ from wbcore import viewsets
18
+ from wbcore.utils.date import get_date_interval_from_request
19
+
20
+
21
+ class EntryAccountingInformationRepresentationViewSet(viewsets.RepresentationViewSet):
22
+ search_fields = ("entry__computed_str",)
23
+ queryset = EntryAccountingInformation.objects.all()
24
+ serializer_class = EntryAccountingInformationRepresentationSerializer
25
+
26
+ def get_queryset(self):
27
+ return EntryAccountingInformation.objects.filter_for_user(self.request.user)
28
+
29
+
30
+ class EntryAccountingInformationModelViewSet(viewsets.ModelViewSet):
31
+ IDENTIFIER = "wbaccounting:entryaccountinginformation"
32
+ filterset_fields = {
33
+ "entry": ["exact"],
34
+ "tax_id": ["exact", "icontains"],
35
+ "vat": ["gte", "exact", "lte"],
36
+ "default_currency": ["gte", "exact", "lte"],
37
+ "send_mail": ["exact"],
38
+ "counterparty_is_private": ["exact"],
39
+ "exempt_users": ["exact"],
40
+ }
41
+
42
+ serializer_class = EntryAccountingInformationModelSerializer
43
+ queryset = EntryAccountingInformation.objects.all()
44
+
45
+ search_fields = ["entry__computed_str"]
46
+ ordering_fields = ["tax_id", "vat", "send_mail", "counterparty_is_private"]
47
+ ordering = ["id"]
48
+
49
+ display_config_class = EntryAccountingInformationDisplayConfig
50
+ title_config_class = EntryAccountingInformationTitleConfig
51
+ button_config_class = EntryAccountingInformationButtonConfig
52
+
53
+ def get_queryset(self) -> QuerySet[EntryAccountingInformation]:
54
+ return (
55
+ EntryAccountingInformation.objects.filter_for_user(self.request.user)
56
+ .select_related(
57
+ "entry",
58
+ "default_currency",
59
+ "default_invoice_type",
60
+ )
61
+ .prefetch_related(
62
+ "exempt_users",
63
+ "email_to",
64
+ "email_cc",
65
+ "email_bcc",
66
+ )
67
+ )
68
+
69
+ @action(detail=True, methods=["POST"], permission_classes=[IsAuthenticated])
70
+ def generate_booking_entries(self, request, pk):
71
+ if (
72
+ (user := request.user)
73
+ and (user.is_superuser or user.has_perm("wbaccounting.can_generate_booking_entries"))
74
+ and (eai := self.get_object())
75
+ and eai is not None
76
+ ):
77
+ start, end = get_date_interval_from_request(request, request_type="POST")
78
+ eai.generate_booking_entries(start, end) # type: ignore
79
+ return Response(
80
+ {"__notification": {"title": "Booking Entries are being generated in the background."}},
81
+ status=status.HTTP_200_OK,
82
+ )
83
+
84
+ @action(detail=False, methods=["POST"], permission_classes=[IsAuthenticated])
85
+ def generate_booking_entries_for_counterparties(self, request, pk=None):
86
+ eais = EntryAccountingInformation.objects.filter(entry__id__in=request.POST.get("counterparties").split(","))
87
+
88
+ if (
89
+ (user := request.user)
90
+ and (user.is_superuser or user.has_perm("wbaccounting.can_generate_booking_entries"))
91
+ and eais.exists()
92
+ ):
93
+ for eai in eais:
94
+ start, end = get_date_interval_from_request(request, request_type="POST")
95
+ eai.generate_booking_entries(start, end) # type: ignore
96
+ return Response(
97
+ {"__notification": {"title": "Booking Entries are being generated in the background."}},
98
+ status=status.HTTP_200_OK,
99
+ )
100
+
101
+ @action(detail=False, methods=["POST"], permission_classes=[IsAuthenticated])
102
+ def generate_invoices_for_counterparties(self, request, pk=None):
103
+ try:
104
+ eais = EntryAccountingInformation.objects.filter(
105
+ entry__id__in=request.POST.get("counterparties").split(",")
106
+ )
107
+ except AttributeError:
108
+ eais = EntryAccountingInformation.objects.all()
109
+
110
+ if (
111
+ (user := request.user)
112
+ and (user.is_superuser or user.has_perm("wbaccounting.can_generate_invoice"))
113
+ and eais.exists()
114
+ ):
115
+ eais = eais.annotate(
116
+ has_open_bookings=Exists(
117
+ BookingEntry.objects.filter(
118
+ counterparty=OuterRef("entry"),
119
+ invoice__isnull=True,
120
+ payment_date__isnull=True,
121
+ )
122
+ )
123
+ ).filter(has_open_bookings=True)
124
+ for eai in eais:
125
+ Invoice.objects.create_for_counterparty(eai.entry, invoice_date=date.today())
126
+ return Response(
127
+ {"__notification": {"title": "Invoices are being generated in the background."}},
128
+ status=status.HTTP_200_OK,
129
+ )
130
+
131
+ @action(detail=True, methods=["POST"], permission_classes=[IsAuthenticated])
132
+ def invoice_booking_entries(self, request, pk):
133
+ if (
134
+ (user := request.user)
135
+ and (user.is_superuser or user.has_perm("wbaccounting.can_generate_invoice"))
136
+ and (eai := self.get_object())
137
+ and eai is not None
138
+ ):
139
+ Invoice.objects.create_for_counterparty(eai.entry, invoice_date=date.today())
140
+ return Response(
141
+ {"__notification": {"title": "Booking Entries are being generated in the background."}},
142
+ status=status.HTTP_200_OK,
143
+ )