wbaccounting 2.2.1__tar.gz
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-2.2.1/.gitignore +181 -0
- wbaccounting-2.2.1/PKG-INFO +8 -0
- wbaccounting-2.2.1/pyproject.toml +31 -0
- wbaccounting-2.2.1/wbaccounting/__init__.py +1 -0
- wbaccounting-2.2.1/wbaccounting/admin/__init__.py +5 -0
- wbaccounting-2.2.1/wbaccounting/admin/booking_entry.py +53 -0
- wbaccounting-2.2.1/wbaccounting/admin/entry_accounting_information.py +10 -0
- wbaccounting-2.2.1/wbaccounting/admin/invoice.py +26 -0
- wbaccounting-2.2.1/wbaccounting/admin/invoice_type.py +8 -0
- wbaccounting-2.2.1/wbaccounting/admin/transactions.py +16 -0
- wbaccounting-2.2.1/wbaccounting/apps.py +5 -0
- wbaccounting-2.2.1/wbaccounting/dynamic_preferences_registry.py +107 -0
- wbaccounting-2.2.1/wbaccounting/factories/__init__.py +10 -0
- wbaccounting-2.2.1/wbaccounting/factories/booking_entry.py +21 -0
- wbaccounting-2.2.1/wbaccounting/factories/entry_accounting_information.py +46 -0
- wbaccounting-2.2.1/wbaccounting/factories/invoice.py +43 -0
- wbaccounting-2.2.1/wbaccounting/factories/transactions.py +32 -0
- wbaccounting-2.2.1/wbaccounting/files/__init__.py +0 -0
- wbaccounting-2.2.1/wbaccounting/files/invoice_document_file.py +134 -0
- wbaccounting-2.2.1/wbaccounting/files/utils.py +331 -0
- wbaccounting-2.2.1/wbaccounting/fixtures/accounting.json +578 -0
- wbaccounting-2.2.1/wbaccounting/generators/__init__.py +6 -0
- wbaccounting-2.2.1/wbaccounting/generators/base.py +120 -0
- wbaccounting-2.2.1/wbaccounting/io/handlers/__init__.py +0 -0
- wbaccounting-2.2.1/wbaccounting/io/handlers/transactions.py +32 -0
- wbaccounting-2.2.1/wbaccounting/io/parsers/__init__.py +0 -0
- wbaccounting-2.2.1/wbaccounting/io/parsers/societe_generale_lux.py +49 -0
- wbaccounting-2.2.1/wbaccounting/io/parsers/societe_generale_lux_prenotification.py +60 -0
- wbaccounting-2.2.1/wbaccounting/migrations/0001_initial_squashed_squashed_0005_alter_bookingentry_counterparty_and_more.py +284 -0
- wbaccounting-2.2.1/wbaccounting/migrations/0006_alter_invoice_status.py +30 -0
- wbaccounting-2.2.1/wbaccounting/migrations/0007_alter_invoice_options.py +23 -0
- wbaccounting-2.2.1/wbaccounting/migrations/0008_alter_invoice_options.py +20 -0
- wbaccounting-2.2.1/wbaccounting/migrations/0009_invoicetype_alter_bookingentry_options_and_more.py +366 -0
- wbaccounting-2.2.1/wbaccounting/migrations/0010_alter_bookingentry_options.py +20 -0
- wbaccounting-2.2.1/wbaccounting/migrations/0011_transaction.py +103 -0
- wbaccounting-2.2.1/wbaccounting/migrations/0012_entryaccountinginformation_external_invoice_users.py +25 -0
- wbaccounting-2.2.1/wbaccounting/migrations/__init__.py +0 -0
- wbaccounting-2.2.1/wbaccounting/models/__init__.py +6 -0
- wbaccounting-2.2.1/wbaccounting/models/booking_entry.py +167 -0
- wbaccounting-2.2.1/wbaccounting/models/entry_accounting_information.py +157 -0
- wbaccounting-2.2.1/wbaccounting/models/invoice.py +467 -0
- wbaccounting-2.2.1/wbaccounting/models/invoice_type.py +30 -0
- wbaccounting-2.2.1/wbaccounting/models/model_tasks.py +71 -0
- wbaccounting-2.2.1/wbaccounting/models/transactions.py +112 -0
- wbaccounting-2.2.1/wbaccounting/permissions.py +6 -0
- wbaccounting-2.2.1/wbaccounting/processors/__init__.py +0 -0
- wbaccounting-2.2.1/wbaccounting/processors/dummy_processor.py +5 -0
- wbaccounting-2.2.1/wbaccounting/serializers/__init__.py +12 -0
- wbaccounting-2.2.1/wbaccounting/serializers/booking_entry.py +78 -0
- wbaccounting-2.2.1/wbaccounting/serializers/consolidated_invoice.py +109 -0
- wbaccounting-2.2.1/wbaccounting/serializers/entry_accounting_information.py +149 -0
- wbaccounting-2.2.1/wbaccounting/serializers/invoice.py +95 -0
- wbaccounting-2.2.1/wbaccounting/serializers/invoice_type.py +16 -0
- wbaccounting-2.2.1/wbaccounting/serializers/transactions.py +50 -0
- wbaccounting-2.2.1/wbaccounting/tests/__init__.py +0 -0
- wbaccounting-2.2.1/wbaccounting/tests/conftest.py +65 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_displays/__init__.py +0 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_displays/test_booking_entries.py +1 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_models/__init__.py +0 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_models/test_booking_entries.py +119 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_models/test_entry_accounting_information.py +81 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_models/test_invoice_types.py +21 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_models/test_invoices.py +73 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_models/test_transactions.py +40 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_processors.py +28 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_serializers/__init__.py +0 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_serializers/test_booking_entries.py +69 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_serializers/test_entry_accounting_information.py +64 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_serializers/test_invoice_types.py +35 -0
- wbaccounting-2.2.1/wbaccounting/tests/test_serializers/test_transactions.py +72 -0
- wbaccounting-2.2.1/wbaccounting/urls.py +68 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/__init__.py +12 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/booking_entry.py +61 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/buttons/__init__.py +3 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/buttons/booking_entry.py +15 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/buttons/entry_accounting_information.py +100 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/buttons/invoice.py +65 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/cashflows.py +124 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/display/__init__.py +8 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/display/booking_entry.py +58 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/display/cashflows.py +58 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/display/entry_accounting_information.py +91 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/display/invoice.py +218 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/display/invoice_type.py +19 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/display/transactions.py +35 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/endpoints/__init__.py +1 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/endpoints/invoice.py +6 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/entry_accounting_information.py +143 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/invoice.py +277 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/invoice_type.py +25 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/menu/__init__.py +6 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/menu/booking_entry.py +15 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/menu/cashflows.py +10 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/menu/entry_accounting_information.py +11 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/menu/invoice.py +15 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/menu/invoice_type.py +15 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/menu/transactions.py +15 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/titles/__init__.py +4 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/titles/booking_entry.py +12 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/titles/entry_accounting_information.py +12 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/titles/invoice.py +23 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/titles/invoice_type.py +12 -0
- wbaccounting-2.2.1/wbaccounting/viewsets/transactions.py +34 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
~
|
|
2
|
+
|
|
3
|
+
# Docker volumes
|
|
4
|
+
volumes/
|
|
5
|
+
# Poetry auth file
|
|
6
|
+
auth.toml
|
|
7
|
+
|
|
8
|
+
media/*
|
|
9
|
+
media/
|
|
10
|
+
mediafiles/
|
|
11
|
+
mediafiles/*
|
|
12
|
+
test/*
|
|
13
|
+
staticfiles/*
|
|
14
|
+
staticfiles/
|
|
15
|
+
#
|
|
16
|
+
# Byte-compiled / optimized / DLL files
|
|
17
|
+
__pycache__/
|
|
18
|
+
*.py[cod]
|
|
19
|
+
*$py.class
|
|
20
|
+
|
|
21
|
+
# C extensions
|
|
22
|
+
*.so
|
|
23
|
+
|
|
24
|
+
# Distribution / packaging
|
|
25
|
+
.Python
|
|
26
|
+
build/
|
|
27
|
+
develop-eggs/
|
|
28
|
+
dist/
|
|
29
|
+
info/
|
|
30
|
+
downloads/
|
|
31
|
+
eggs/
|
|
32
|
+
.eggs/
|
|
33
|
+
lib/
|
|
34
|
+
lib64/
|
|
35
|
+
parts/
|
|
36
|
+
sdist/
|
|
37
|
+
var/
|
|
38
|
+
wheels/
|
|
39
|
+
share/python-wheels/
|
|
40
|
+
*.egg-info/
|
|
41
|
+
.installed.cfg
|
|
42
|
+
*.egg
|
|
43
|
+
MANIFEST
|
|
44
|
+
|
|
45
|
+
# PyInstaller
|
|
46
|
+
# Usually these files are written by a python script from a template
|
|
47
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
48
|
+
*.manifest
|
|
49
|
+
*.spec
|
|
50
|
+
|
|
51
|
+
# Installer logs
|
|
52
|
+
pip-log.txt
|
|
53
|
+
pip-delete-this-directory.txt
|
|
54
|
+
|
|
55
|
+
# Unit test / coverage reports
|
|
56
|
+
htmlcov/
|
|
57
|
+
.tox/
|
|
58
|
+
.nox/
|
|
59
|
+
.coverage
|
|
60
|
+
.coverage.*
|
|
61
|
+
.cache
|
|
62
|
+
.dccache
|
|
63
|
+
nosetests.xml
|
|
64
|
+
coverage.xml
|
|
65
|
+
*.cover
|
|
66
|
+
*.py,cover
|
|
67
|
+
.hypothesis/
|
|
68
|
+
.pytest_cache/
|
|
69
|
+
cover/
|
|
70
|
+
report.xml
|
|
71
|
+
*/report.xml
|
|
72
|
+
|
|
73
|
+
# Translations
|
|
74
|
+
*.mo
|
|
75
|
+
*.pot
|
|
76
|
+
|
|
77
|
+
# Django stuff:
|
|
78
|
+
*.log
|
|
79
|
+
local_settings.py
|
|
80
|
+
*.sqlite3
|
|
81
|
+
db.sqlite3-journal
|
|
82
|
+
|
|
83
|
+
# Flask stuff:
|
|
84
|
+
instance/
|
|
85
|
+
.webassets-cache
|
|
86
|
+
|
|
87
|
+
# Scrapy stuff:
|
|
88
|
+
.scrapy
|
|
89
|
+
|
|
90
|
+
# Sphinx documentation
|
|
91
|
+
docs/_build/
|
|
92
|
+
|
|
93
|
+
# PyBuilder
|
|
94
|
+
.pybuilder/
|
|
95
|
+
target/
|
|
96
|
+
|
|
97
|
+
# Jupyter Notebook
|
|
98
|
+
.ipynb_checkpoints
|
|
99
|
+
*.ipynb
|
|
100
|
+
# IPython
|
|
101
|
+
profile_default/
|
|
102
|
+
ipython_config.py
|
|
103
|
+
|
|
104
|
+
# pyenv
|
|
105
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
106
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
107
|
+
# .python-version
|
|
108
|
+
|
|
109
|
+
# pipenv
|
|
110
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
111
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
112
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
113
|
+
# install all needed dependencies.
|
|
114
|
+
#Pipfile.lock
|
|
115
|
+
|
|
116
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
|
117
|
+
__pypackages__/
|
|
118
|
+
|
|
119
|
+
# Celery stuff
|
|
120
|
+
celerybeat-schedule
|
|
121
|
+
celerybeat.pid
|
|
122
|
+
|
|
123
|
+
# SageMath parsed files
|
|
124
|
+
*.sage.py
|
|
125
|
+
|
|
126
|
+
# Environments
|
|
127
|
+
.env
|
|
128
|
+
.envrc
|
|
129
|
+
.venv
|
|
130
|
+
env/
|
|
131
|
+
venv/
|
|
132
|
+
ENV/
|
|
133
|
+
env.bak/
|
|
134
|
+
venv.bak/
|
|
135
|
+
.vscode/
|
|
136
|
+
.idea/
|
|
137
|
+
.idea.bkp/
|
|
138
|
+
|
|
139
|
+
# Spyder project settings
|
|
140
|
+
.spyderproject
|
|
141
|
+
.spyproject
|
|
142
|
+
|
|
143
|
+
# Rope project settings
|
|
144
|
+
.ropeproject
|
|
145
|
+
|
|
146
|
+
# mkdocs documentation
|
|
147
|
+
/site
|
|
148
|
+
|
|
149
|
+
# mypy
|
|
150
|
+
.mypy_cache/
|
|
151
|
+
.dmypy.json
|
|
152
|
+
dmypy.json
|
|
153
|
+
|
|
154
|
+
# Pyre type checker
|
|
155
|
+
.pyre/
|
|
156
|
+
|
|
157
|
+
# pytype static type analyzer
|
|
158
|
+
.pytype/
|
|
159
|
+
crm/
|
|
160
|
+
# Cython debug symbols
|
|
161
|
+
cython_debug/
|
|
162
|
+
|
|
163
|
+
# Gitlab Runner
|
|
164
|
+
builds
|
|
165
|
+
builds/
|
|
166
|
+
|
|
167
|
+
# Integrator Office 365 : reverse proxy tunnel for outlook365
|
|
168
|
+
ngrok
|
|
169
|
+
*/ngrok
|
|
170
|
+
/modules/**/system/
|
|
171
|
+
|
|
172
|
+
/modules/wbmailing/files/*
|
|
173
|
+
/modules/wbmailing/mailing/*
|
|
174
|
+
|
|
175
|
+
/projects/*/requirements.txt
|
|
176
|
+
public
|
|
177
|
+
|
|
178
|
+
# Ignore archive localization generated folder
|
|
179
|
+
backend/modules/**/archive/*
|
|
180
|
+
**/**/requirements.txt
|
|
181
|
+
CHANGELOG-*
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: wbaccounting
|
|
3
|
+
Version: 2.2.1
|
|
4
|
+
Summary: A workbench module for managing invoicing and simple accounting.
|
|
5
|
+
Author-email: Christopher Wittlinger <c.wittlinger@stainly.com>
|
|
6
|
+
Requires-Dist: reportlab==3.*
|
|
7
|
+
Requires-Dist: schwifty==2024.5.*
|
|
8
|
+
Requires-Dist: wbcore
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "wbaccounting"
|
|
3
|
+
description = "A workbench module for managing invoicing and simple accounting."
|
|
4
|
+
authors = [{ name = "Christopher Wittlinger", email = "c.wittlinger@stainly.com"}]
|
|
5
|
+
dynamic = ["version"]
|
|
6
|
+
|
|
7
|
+
dependencies = [
|
|
8
|
+
"wbcore",
|
|
9
|
+
"reportlab == 3.*",
|
|
10
|
+
"schwifty == 2024.5.*",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[tool.uv.sources]
|
|
14
|
+
wbcore = { workspace = true }
|
|
15
|
+
|
|
16
|
+
[tool.uv]
|
|
17
|
+
package = true
|
|
18
|
+
|
|
19
|
+
[tool.hatch.version]
|
|
20
|
+
path = "../../pyproject.toml"
|
|
21
|
+
|
|
22
|
+
[tool.hatch.build.targets.sdist]
|
|
23
|
+
include = ["wbaccounting/*"]
|
|
24
|
+
|
|
25
|
+
[tool.hatch.build.targets.wheel]
|
|
26
|
+
packages = ["wbaccounting"]
|
|
27
|
+
only-packages = true
|
|
28
|
+
|
|
29
|
+
[build-system]
|
|
30
|
+
requires = ["hatchling"]
|
|
31
|
+
build-backend = "hatchling.build"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
from .booking_entry import BookingEntryInline, BookingEntryModelAdmin
|
|
2
|
+
from .entry_accounting_information import EntryAccountingInformationModelAdmin
|
|
3
|
+
from .invoice_type import InvoiceTypeModelAdmin
|
|
4
|
+
from .invoice import InvoiceModelAdmin
|
|
5
|
+
from .transactions import TransactionModelAdmin
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
|
|
3
|
+
from django.contrib import admin
|
|
4
|
+
from wbaccounting.models import BookingEntry, Invoice
|
|
5
|
+
from wbcore.contrib.directory.models import Entry
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BookingEntryInline(admin.TabularInline):
|
|
9
|
+
model = BookingEntry
|
|
10
|
+
fields = ("booking_date", "net_value", "vat")
|
|
11
|
+
ordering = ("vat", "title")
|
|
12
|
+
|
|
13
|
+
def has_add_permission(self, request, obj=None):
|
|
14
|
+
return False
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@admin.register(BookingEntry)
|
|
18
|
+
class BookingEntryModelAdmin(admin.ModelAdmin):
|
|
19
|
+
list_display = (
|
|
20
|
+
"title",
|
|
21
|
+
"booking_date",
|
|
22
|
+
"net_value",
|
|
23
|
+
"vat",
|
|
24
|
+
"currency",
|
|
25
|
+
"payment_date",
|
|
26
|
+
"counterparty",
|
|
27
|
+
"reference_date",
|
|
28
|
+
)
|
|
29
|
+
search_fields = ["counterparty__computed_str"]
|
|
30
|
+
autocomplete_fields = ("counterparty", "currency")
|
|
31
|
+
|
|
32
|
+
# TODO: Remove or adjust.
|
|
33
|
+
def create_invoice(self, request, queryset):
|
|
34
|
+
counterparty = list(set(queryset.values_list("counterparty__id", flat=True)))
|
|
35
|
+
if len(counterparty) == 1:
|
|
36
|
+
counterparty = Entry.objects.get(id=counterparty[0])
|
|
37
|
+
invoice = Invoice.objects.create(
|
|
38
|
+
title=f"Rebate Invoice {counterparty.computed_str} ({queryset.earliest('from_date').from_date:%d.%m.%Y} - {queryset.latest('to_date').to_date:%d.%m.%Y})",
|
|
39
|
+
invoice_date=date.today(),
|
|
40
|
+
reference_date=date.today(),
|
|
41
|
+
counterparty=counterparty,
|
|
42
|
+
invoice_currency=counterparty.entry_accounting_information.default_currency,
|
|
43
|
+
is_counterparty_invoice=True,
|
|
44
|
+
)
|
|
45
|
+
queryset.update(invoice=invoice)
|
|
46
|
+
invoice.save()
|
|
47
|
+
|
|
48
|
+
else:
|
|
49
|
+
print("-------------------------------") # noqa: T201
|
|
50
|
+
print(f"Too many counterparties selected ({len(counterparty)})") # noqa: T201
|
|
51
|
+
print("-------------------------------") # noqa: T201
|
|
52
|
+
|
|
53
|
+
actions = [create_invoice]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
from wbaccounting.models import EntryAccountingInformation
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@admin.register(EntryAccountingInformation)
|
|
6
|
+
class EntryAccountingInformationModelAdmin(admin.ModelAdmin):
|
|
7
|
+
list_display = ("entry", "send_mail", "counterparty_is_private")
|
|
8
|
+
autocomplete_fields = ["default_currency", "entry", "email_to", "email_cc", "email_bcc", "exempt_users"]
|
|
9
|
+
|
|
10
|
+
search_fields = ["entry__computed_str"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
from wbaccounting.admin import BookingEntryInline
|
|
3
|
+
from wbaccounting.models import Invoice
|
|
4
|
+
from wbcore.contrib.documents.admin import DocumentInLine
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@admin.register(Invoice)
|
|
8
|
+
class InvoiceModelAdmin(admin.ModelAdmin):
|
|
9
|
+
fsm_field = ["status"]
|
|
10
|
+
search_fields = ("counterparty__computed_str", "title", "invoice_type__name")
|
|
11
|
+
list_display = (
|
|
12
|
+
"status",
|
|
13
|
+
"title",
|
|
14
|
+
"gross_value",
|
|
15
|
+
"net_value",
|
|
16
|
+
"invoice_date",
|
|
17
|
+
"invoice_currency",
|
|
18
|
+
"counterparty",
|
|
19
|
+
"invoice_type",
|
|
20
|
+
"reference_date",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
autocomplete_fields = ("counterparty",)
|
|
24
|
+
inlines = [BookingEntryInline, DocumentInLine]
|
|
25
|
+
|
|
26
|
+
raw_id_fields = ["counterparty", "invoice_currency", "invoice_type"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
from wbaccounting.models import Transaction
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@admin.register(Transaction)
|
|
6
|
+
class TransactionModelAdmin(admin.ModelAdmin):
|
|
7
|
+
list_display = ["booking_date", "value_date", "value", "bank_account", "prenotification"]
|
|
8
|
+
|
|
9
|
+
raw_id_fields = ["import_source"]
|
|
10
|
+
|
|
11
|
+
autocomplete_fields = [
|
|
12
|
+
"bank_account",
|
|
13
|
+
"from_bank_account",
|
|
14
|
+
"to_bank_account",
|
|
15
|
+
"currency",
|
|
16
|
+
]
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from dynamic_preferences.preferences import Section
|
|
3
|
+
from dynamic_preferences.registries import global_preferences_registry
|
|
4
|
+
from dynamic_preferences.types import (
|
|
5
|
+
IntegerPreference,
|
|
6
|
+
LongStringPreference,
|
|
7
|
+
ModelMultipleChoicePreference,
|
|
8
|
+
StringPreference,
|
|
9
|
+
)
|
|
10
|
+
from wbcore.contrib.currency.models import Currency
|
|
11
|
+
from wbcore.contrib.directory.models import Person
|
|
12
|
+
|
|
13
|
+
accounting_section = Section("wbaccounting")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def format_invoice_number(number):
|
|
17
|
+
global_preferences = global_preferences_registry.manager()
|
|
18
|
+
ts = global_preferences["wbaccounting__invoice_thousand_seperator"]
|
|
19
|
+
ds = global_preferences["wbaccounting__invoice_decimal_seperator"]
|
|
20
|
+
if not number:
|
|
21
|
+
number = 0
|
|
22
|
+
s = "{:,.2f}".format(number)
|
|
23
|
+
s = s.replace(",", "//")
|
|
24
|
+
s = s.replace(".", "\\")
|
|
25
|
+
|
|
26
|
+
return s.replace("//", ts).replace("\\", ds)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@global_preferences_registry.register
|
|
30
|
+
class DefaultEntryAccountingInformationCurrency(StringPreference):
|
|
31
|
+
section = accounting_section
|
|
32
|
+
name = "default_entry_account_information_currency_key"
|
|
33
|
+
default = "CHF"
|
|
34
|
+
|
|
35
|
+
verbose_name = "The currency key used for the default currency of newly created entry accounting informations."
|
|
36
|
+
|
|
37
|
+
def validate(self, value):
|
|
38
|
+
if not Currency.objects.filter(key=value).exists():
|
|
39
|
+
return ValueError("The specified currency key is not valid")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@global_preferences_registry.register
|
|
43
|
+
class ExternalEmailAddress(StringPreference):
|
|
44
|
+
section = accounting_section
|
|
45
|
+
name = "external_email_address"
|
|
46
|
+
default = ""
|
|
47
|
+
|
|
48
|
+
verbose_name = "External Email Address"
|
|
49
|
+
help_text = "The Email Address used to send Invoices to the external party."
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@global_preferences_registry.register
|
|
53
|
+
class InvoiceEmailBody(LongStringPreference):
|
|
54
|
+
section = accounting_section
|
|
55
|
+
name = "invoice_email_body"
|
|
56
|
+
default = ""
|
|
57
|
+
|
|
58
|
+
verbose_name = "The default E-Mail Body used for emailing Invoices"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@global_preferences_registry.register
|
|
62
|
+
class InvoiceThousandSeperatorPreference(StringPreference):
|
|
63
|
+
section = accounting_section
|
|
64
|
+
name = "invoice_thousand_seperator"
|
|
65
|
+
default = ","
|
|
66
|
+
|
|
67
|
+
verbose_name = "Invoice Thousand Seperator"
|
|
68
|
+
help_text = "Thousand Seperator for Invoices"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@global_preferences_registry.register
|
|
72
|
+
class InvoiceDecimalSeperatorPreference(StringPreference):
|
|
73
|
+
section = accounting_section
|
|
74
|
+
name = "invoice_decimal_seperator"
|
|
75
|
+
default = "."
|
|
76
|
+
|
|
77
|
+
verbose_name = "Invoice Decimal Seperator"
|
|
78
|
+
help_text = "Decimal Seperator for Invoices"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@global_preferences_registry.register
|
|
82
|
+
class InvoiceSignerPreference(ModelMultipleChoicePreference):
|
|
83
|
+
section = accounting_section
|
|
84
|
+
name = "invoice_signers"
|
|
85
|
+
queryset = Person.objects.all()
|
|
86
|
+
default = None
|
|
87
|
+
verbose_name = "Invoice Signers"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@global_preferences_registry.register
|
|
91
|
+
class InvoiceCompanyPreference(IntegerPreference):
|
|
92
|
+
section = accounting_section
|
|
93
|
+
name = "invoice_company"
|
|
94
|
+
default = 0
|
|
95
|
+
|
|
96
|
+
verbose_name = "Invoice Company"
|
|
97
|
+
help_text = "The PK of the company who issues the invoices"
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@global_preferences_registry.register
|
|
101
|
+
class DefaultFromEmailAddressPreference(StringPreference):
|
|
102
|
+
section = accounting_section
|
|
103
|
+
name = "default_from_email_address"
|
|
104
|
+
default = settings.DEFAULT_FROM_EMAIL
|
|
105
|
+
|
|
106
|
+
verbose_name = "The default from email address"
|
|
107
|
+
help_text = "The default from email address used to send invoice"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from .booking_entry import BookingEntryFactory
|
|
2
|
+
from .entry_accounting_information import (
|
|
3
|
+
CompanyAccountingFactory,
|
|
4
|
+
EntryAccountingInformationFactory,
|
|
5
|
+
)
|
|
6
|
+
from .invoice import (
|
|
7
|
+
InvoiceFactory,
|
|
8
|
+
InvoiceTypeFactory,
|
|
9
|
+
)
|
|
10
|
+
from .transactions import TransactionFactory, LocalCurrencyTransactionFactory
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import factory
|
|
2
|
+
from factory.fuzzy import FuzzyDecimal
|
|
3
|
+
from wbaccounting.models import BookingEntry
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BookingEntryFactory(factory.django.DjangoModelFactory):
|
|
7
|
+
class Meta: # type: ignore
|
|
8
|
+
model = BookingEntry
|
|
9
|
+
|
|
10
|
+
# resolved
|
|
11
|
+
title = factory.Faker("text", max_nb_chars=64)
|
|
12
|
+
booking_date = factory.Faker("date_between", start_date="+2d", end_date="+3d")
|
|
13
|
+
payment_date = factory.Faker("date_object")
|
|
14
|
+
reference_date = factory.Faker("date_object")
|
|
15
|
+
gross_value = factory.Faker("pydecimal", right_digits=4, min_value=0, max_value=99999999999)
|
|
16
|
+
net_value = factory.Faker("pydecimal", right_digits=4, min_value=0, max_value=99999999999)
|
|
17
|
+
vat = FuzzyDecimal(0, 0.9, 4)
|
|
18
|
+
currency = factory.SelfAttribute("invoice.invoice_currency")
|
|
19
|
+
|
|
20
|
+
invoice = factory.SubFactory("wbaccounting.factories.InvoiceFactory")
|
|
21
|
+
counterparty = factory.SubFactory("wbcore.contrib.directory.factories.EntryFactory")
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import factory
|
|
2
|
+
from factory.fuzzy import FuzzyDecimal
|
|
3
|
+
from wbaccounting.models import EntryAccountingInformation
|
|
4
|
+
from wbcore.contrib.directory.factories import CompanyFactory, EmailContactFactory
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EntryAccountingInformationFactory(factory.django.DjangoModelFactory):
|
|
8
|
+
class Meta: # type: ignore
|
|
9
|
+
model = EntryAccountingInformation
|
|
10
|
+
|
|
11
|
+
entry = factory.SubFactory("wbcore.contrib.directory.factories.EntryFactory")
|
|
12
|
+
tax_id = factory.Faker("text", max_nb_chars=64)
|
|
13
|
+
vat = FuzzyDecimal(0, 0.9, 4)
|
|
14
|
+
send_mail = factory.Faker("pybool")
|
|
15
|
+
counterparty_is_private = False
|
|
16
|
+
|
|
17
|
+
email_body = factory.Faker("paragraph")
|
|
18
|
+
|
|
19
|
+
@factory.post_generation
|
|
20
|
+
def post(self, create, extracted, **kwargs):
|
|
21
|
+
if isinstance(self, EntryAccountingInformationFactory) and not create:
|
|
22
|
+
self.email_to.add(EmailContactFactory.create())
|
|
23
|
+
self.email_cc_add(EmailContactFactory.create())
|
|
24
|
+
self.email_bcc.add(EmailContactFactory.create())
|
|
25
|
+
|
|
26
|
+
if isinstance(self, dict):
|
|
27
|
+
self["email_to"] = [EmailContactFactory.create().id]
|
|
28
|
+
self["email_cc"] = [EmailContactFactory.create().id]
|
|
29
|
+
self["email_bcc"] = [EmailContactFactory.create().id]
|
|
30
|
+
|
|
31
|
+
@factory.post_generation
|
|
32
|
+
def exempt_users(self, create, extracted, **kwargs):
|
|
33
|
+
if not create:
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
if extracted:
|
|
37
|
+
for user in extracted:
|
|
38
|
+
self.exempt_users.add(user)
|
|
39
|
+
|
|
40
|
+
default_currency = factory.SubFactory("wbcore.contrib.currency.factories.CurrencyFactory")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CompanyAccountingFactory(CompanyFactory):
|
|
44
|
+
entry_accounting_information = factory.RelatedFactory(
|
|
45
|
+
"wbaccounting.factories.EntryAccountingInformationFactory", "entry"
|
|
46
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import factory
|
|
2
|
+
from wbaccounting.models import Invoice, InvoiceType
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class InvoiceFactory(factory.django.DjangoModelFactory):
|
|
6
|
+
class Meta: # type: ignore
|
|
7
|
+
model = Invoice
|
|
8
|
+
|
|
9
|
+
title = factory.Faker("text", max_nb_chars=64)
|
|
10
|
+
invoice_date = factory.Faker("date_object")
|
|
11
|
+
reference_date = factory.Faker("date_object")
|
|
12
|
+
invoice_currency = factory.SubFactory("wbcore.contrib.currency.factories.CurrencyUSDFactory")
|
|
13
|
+
|
|
14
|
+
counterparty = factory.SubFactory("wbcore.contrib.directory.factories.EntryFactory")
|
|
15
|
+
invoice_type = factory.SubFactory("wbaccounting.factories.InvoiceTypeFactory")
|
|
16
|
+
|
|
17
|
+
text_above = factory.Faker("text")
|
|
18
|
+
text_below = factory.Faker("text")
|
|
19
|
+
|
|
20
|
+
# @factory.post_generation
|
|
21
|
+
# def invoice_document(self, create, extracted, **kwargs):
|
|
22
|
+
# self.refresh_invoice_document(override_status=True)
|
|
23
|
+
|
|
24
|
+
# is_counterparty_invoice
|
|
25
|
+
|
|
26
|
+
# @classmethod
|
|
27
|
+
# def _create(cls, model_class, *args, **kwargs):
|
|
28
|
+
# company = CompanyFactory()
|
|
29
|
+
# signee = PersonSignatureFactory()
|
|
30
|
+
# global_preferences_registry.manager()["wbaccounting__invoice_company"] = company.id
|
|
31
|
+
# global_preferences_registry.manager()["wbaccounting__invoice_signers"] = Person.objects.filter(id=signee.id)
|
|
32
|
+
# """Override the default ``_create`` with our custom call."""
|
|
33
|
+
# manager = cls._get_manager(model_class)
|
|
34
|
+
# # The default would use ``manager.create(*args, **kwargs)``
|
|
35
|
+
# return manager.create(*args, **kwargs)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class InvoiceTypeFactory(factory.django.DjangoModelFactory):
|
|
39
|
+
name = factory.Sequence(lambda n: f"Invoice Type {n}")
|
|
40
|
+
processor = factory.Faker("text", max_nb_chars=64)
|
|
41
|
+
|
|
42
|
+
class Meta: # type: ignore
|
|
43
|
+
model = InvoiceType
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
|
|
3
|
+
import factory
|
|
4
|
+
from wbaccounting.models import Transaction
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AbstractTransactionFactory(factory.django.DjangoModelFactory):
|
|
8
|
+
class Meta:
|
|
9
|
+
model = Transaction
|
|
10
|
+
abstract = True
|
|
11
|
+
|
|
12
|
+
booking_date = factory.Faker("date_between", start_date="+2d", end_date="+3d")
|
|
13
|
+
value_date = factory.Faker("date_object")
|
|
14
|
+
|
|
15
|
+
bank_account = factory.SubFactory("wbcore.contrib.directory.factories.BankingContactFactory")
|
|
16
|
+
from_bank_account = factory.SubFactory("wbcore.contrib.directory.factories.BankingContactFactory")
|
|
17
|
+
to_bank_account = factory.SubFactory("wbcore.contrib.directory.factories.BankingContactFactory")
|
|
18
|
+
|
|
19
|
+
@factory.post_generation
|
|
20
|
+
def set_currency(self, create, extracted, **kwargs):
|
|
21
|
+
if isinstance(self, dict):
|
|
22
|
+
self["currency"] = self["bank_account"].currency
|
|
23
|
+
else:
|
|
24
|
+
self.currency = self.bank_account.currency
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class TransactionFactory(AbstractTransactionFactory):
|
|
28
|
+
value = factory.Faker("pydecimal", min_value=Decimal(0.1), max_value=Decimal(10000000))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class LocalCurrencyTransactionFactory(AbstractTransactionFactory):
|
|
32
|
+
value_local_ccy = factory.Faker("pydecimal", min_value=Decimal(0.1), max_value=Decimal(10000000))
|
|
File without changes
|