diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000000000000000000000000000000000..60317f4987d885fc380d731c6dd8792325342f44 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,215 @@ +module.exports = { + extends: [ + "eslint:recommended", + "plugin:vue/strongly-recommended", + "plugin:@intlify/vue-i18n/recommended", + ], + rules: { + "no-unused-vars": "warn", + "vue/no-unused-vars": "off", + "vue/multi-word-component-names": "off", + "@intlify/vue-i18n/key-format-style": [ + "error", + "snake_case", + { + splitByDots: false, + }, + ], + // "@intlify/vue-i18n/no-unused-keys": ["warn", {}], + "@intlify/vue-i18n/no-raw-text": [ + "error", + { + ignoreNodes: ["v-icon"], + ignorePattern: "^[-–—·#:()\\[\\]&\\.\\s]+$", + }, + ], + // Fixes for prettier (avoid eslint-config-prettier) + // The following rules can be used in some cases. See the README for more + // information. (These are marked with `0` instead of `"off"` so that a + // script can distinguish them.) + curly: 0, + "lines-around-comment": 0, + "max-len": 0, + "no-confusing-arrow": 0, + "no-mixed-operators": 0, + "no-tabs": 0, + "no-unexpected-multiline": 0, + quotes: 0, + "@typescript-eslint/quotes": 0, + "babel/quotes": 0, + "vue/html-self-closing": 0, + "vue/max-len": 0, + + // The rest are rules that you never need to enable when using Prettier. + "array-bracket-newline": "off", + "array-bracket-spacing": "off", + "array-element-newline": "off", + "arrow-parens": "off", + "arrow-spacing": "off", + "block-spacing": "off", + "brace-style": "off", + "comma-dangle": "off", + "comma-spacing": "off", + "comma-style": "off", + "computed-property-spacing": "off", + "dot-location": "off", + "eol-last": "off", + "func-call-spacing": "off", + "function-call-argument-newline": "off", + "function-paren-newline": "off", + "generator-star": "off", + "generator-star-spacing": "off", + "implicit-arrow-linebreak": "off", + indent: "off", + "jsx-quotes": "off", + "key-spacing": "off", + "keyword-spacing": "off", + "linebreak-style": "off", + "multiline-ternary": "off", + "newline-per-chained-call": "off", + "new-parens": "off", + "no-arrow-condition": "off", + "no-comma-dangle": "off", + "no-extra-parens": "off", + "no-extra-semi": "off", + "no-floating-decimal": "off", + "no-mixed-spaces-and-tabs": "off", + "no-multi-spaces": "off", + "no-multiple-empty-lines": "off", + "no-reserved-keys": "off", + "no-space-before-semi": "off", + "no-trailing-spaces": "off", + "no-whitespace-before-property": "off", + "no-wrap-func": "off", + "nonblock-statement-body-position": "off", + "object-curly-newline": "off", + "object-curly-spacing": "off", + "object-property-newline": "off", + "one-var-declaration-per-line": "off", + "operator-linebreak": "off", + "padded-blocks": "off", + "quote-props": "off", + "rest-spread-spacing": "off", + semi: "off", + "semi-spacing": "off", + "semi-style": "off", + "space-after-function-name": "off", + "space-after-keywords": "off", + "space-before-blocks": "off", + "space-before-function-paren": "off", + "space-before-function-parentheses": "off", + "space-before-keywords": "off", + "space-in-brackets": "off", + "space-in-parens": "off", + "space-infix-ops": "off", + "space-return-throw-case": "off", + "space-unary-ops": "off", + "space-unary-word-ops": "off", + "switch-colon-spacing": "off", + "template-curly-spacing": "off", + "template-tag-spacing": "off", + "unicode-bom": "off", + "wrap-iife": "off", + "wrap-regex": "off", + "yield-star-spacing": "off", + "@babel/object-curly-spacing": "off", + "@babel/semi": "off", + "@typescript-eslint/brace-style": "off", + "@typescript-eslint/comma-dangle": "off", + "@typescript-eslint/comma-spacing": "off", + "@typescript-eslint/func-call-spacing": "off", + "@typescript-eslint/indent": "off", + "@typescript-eslint/keyword-spacing": "off", + "@typescript-eslint/member-delimiter-style": "off", + "@typescript-eslint/no-extra-parens": "off", + "@typescript-eslint/no-extra-semi": "off", + "@typescript-eslint/object-curly-spacing": "off", + "@typescript-eslint/semi": "off", + "@typescript-eslint/space-before-blocks": "off", + "@typescript-eslint/space-before-function-paren": "off", + "@typescript-eslint/space-infix-ops": "off", + "@typescript-eslint/type-annotation-spacing": "off", + "babel/object-curly-spacing": "off", + "babel/semi": "off", + "flowtype/boolean-style": "off", + "flowtype/delimiter-dangle": "off", + "flowtype/generic-spacing": "off", + "flowtype/object-type-curly-spacing": "off", + "flowtype/object-type-delimiter": "off", + "flowtype/quotes": "off", + "flowtype/semi": "off", + "flowtype/space-after-type-colon": "off", + "flowtype/space-before-generic-bracket": "off", + "flowtype/space-before-type-colon": "off", + "flowtype/union-intersection-spacing": "off", + "react/jsx-child-element-spacing": "off", + "react/jsx-closing-bracket-location": "off", + "react/jsx-closing-tag-location": "off", + "react/jsx-curly-newline": "off", + "react/jsx-curly-spacing": "off", + "react/jsx-equals-spacing": "off", + "react/jsx-first-prop-new-line": "off", + "react/jsx-indent": "off", + "react/jsx-indent-props": "off", + "react/jsx-max-props-per-line": "off", + "react/jsx-newline": "off", + "react/jsx-one-expression-per-line": "off", + "react/jsx-props-no-multi-spaces": "off", + "react/jsx-tag-spacing": "off", + "react/jsx-wrap-multilines": "off", + "standard/array-bracket-even-spacing": "off", + "standard/computed-property-even-spacing": "off", + "standard/object-curly-even-spacing": "off", + "unicorn/empty-brace-spaces": "off", + "unicorn/no-nested-ternary": "off", + "unicorn/number-literal-case": "off", + "vue/array-bracket-newline": "off", + "vue/array-bracket-spacing": "off", + "vue/arrow-spacing": "off", + "vue/block-spacing": "off", + "vue/block-tag-newline": "off", + "vue/brace-style": "off", + "vue/comma-dangle": "off", + "vue/comma-spacing": "off", + "vue/comma-style": "off", + "vue/dot-location": "off", + "vue/func-call-spacing": "off", + "vue/html-closing-bracket-newline": "off", + "vue/html-closing-bracket-spacing": "off", + "vue/html-end-tags": "off", + "vue/html-indent": "off", + "vue/html-quotes": "off", + "vue/key-spacing": "off", + "vue/keyword-spacing": "off", + "vue/max-attributes-per-line": "off", + "vue/multiline-html-element-content-newline": "off", + "vue/multiline-ternary": "off", + "vue/mustache-interpolation-spacing": "off", + "vue/no-extra-parens": "off", + "vue/no-multi-spaces": "off", + "vue/no-spaces-around-equal-signs-in-attribute": "off", + "vue/object-curly-newline": "off", + "vue/object-curly-spacing": "off", + "vue/object-property-newline": "off", + "vue/operator-linebreak": "off", + "vue/quote-props": "off", + "vue/script-indent": "off", + "vue/singleline-html-element-content-newline": "off", + "vue/space-in-parens": "off", + "vue/space-infix-ops": "off", + "vue/space-unary-ops": "off", + "vue/template-curly-spacing": "off", + }, + settings: { + "vue-i18n": { + localeDir: "./aleksis/core/frontend/messages/*.{json}", + messageSyntaxVersion: "^8.0.0", + }, + }, + env: { + es2021: true, + }, + parserOptions: { + ecmaVersion: "latest", + }, +}; diff --git a/.gitignore b/.gitignore index 902d52b19777084e9cf9f373502f70b32b4495a6..79b5b76de6f6445254cbf64f7e4fb228ffdf0ab8 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,11 @@ DEADJOE .idea .idea/ +# VSCode +.vscode/ +.history/ +*.code-workspace + # Database db.sqlite3 @@ -62,15 +67,28 @@ docs/_build/ *.aux # Generated files -aleksis/node_modules/ -aleksis/static/ +/cache +/node_modules +.dev-js/node_modules +/static/ +/whoosh_index/ +.vite +.dev-js/.yarn +.dev-js/.pnp.cjs +.dev-js/.pnp.loader.mjs + +# Lock files +poetry.lock +package-lock.json +yarn.lock +.dev-js/yarn.lock +# Tests .coverage .mypy_cache/ .tox/ htmlcov/ + +# Data maintenance_mode_state.txt media/ -package-lock.json - -poetry.lock diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b97f64fa6b8de380209c4d933d18ee374a98e28c..c71fd45f792695baa4f1401c9ba41ce345f6b411 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,15 +1,15 @@ include: - - project: "AlekSIS/official/AlekSIS" - file: /ci/general.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/prepare/lock.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/test/lint.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/test/security.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/build/dist.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/publish/pypi.yml - - project: "AlekSIS/official/AlekSIS" - file: /ci/docker/image.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/general.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/prepare/lock.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/test/lint.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/test/security.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/build/dist.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/publish/pypi.yml + - project: "AlekSIS/official/AlekSIS" + file: /ci/docker/image.yml diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000000000000000000000000000000000..de783fc60a1ca5f6e25204bb7a51eab815bc77ce --- /dev/null +++ b/.prettierignore @@ -0,0 +1,94 @@ +# Byte-compiled / optimized / DLL files +*$py.class +*.py[cod] +__pycache__/ + +# Distribution / packaging +*.egg +*.egg-info/ +.Python +.eggs/ +.installed.cfg +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ + +# Installer logs +pip-delete-this-directory.txt +pip-log.txt + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# pyenv +.python-version + +# Environments +.env +.venv +ENV/ +env/ +venv/ + +# Editors +*~ +DEADJOE +\#*# + +# IntelliJ +.idea +.idea/ + +# Database +db.sqlite3 + +# Sphinx +docs/_build/ + +# TeX +*.aux + +# Generated files +/node_modules/ +/static/ +/whoosh_index/ +poetry.lock + +.coverage +.mypy_cache/ +.tox/ +htmlcov/ +maintenance_mode_state.txt +media/ +package-lock.json +yarn.lock + +# VSCode +.vscode/ +.history/ +*.code-workspace + +/cache + +# Add HTML files to avoid problems with unsupported Django templates +*.html + +# Do not check/reformat generated files +aleksis/core/util/licenses.json +.vite/ + +.pnp.cjs +.pnp.loader.mjs diff --git a/aleksis/apps/tezor/forms.py b/aleksis/apps/tezor/forms.py index e22afc00a68b5146612392d30fb353012f8cace7..e15ad454109751bfaa01aa9c8e73335f10f83ca0 100644 --- a/aleksis/apps/tezor/forms.py +++ b/aleksis/apps/tezor/forms.py @@ -74,7 +74,24 @@ class EditClientForm(ExtensibleForm): class Meta: model = Client - exclude = [] + fields = [ + "name", + "email", + "pledge_enabled", + "sofort_enabled", + "sofort_api_id", + "sofort_api_key", + "sofort_project_id", + "paypal_enabled", + "paypal_client_id", + "paypal_secret", + "paypal_capture", + "sdd_enabled", + "sdd_creditor", + "sdd_creditor_identifier", + "sdd_iban", + "sdd_bic", + ] class EditInvoiceGroupForm(ExtensibleForm): @@ -83,4 +100,4 @@ class EditInvoiceGroupForm(ExtensibleForm): class Meta: model = InvoiceGroup - exclude = ["client"] + fields = ["name", "template_name"] diff --git a/aleksis/apps/tezor/frontend/index.js b/aleksis/apps/tezor/frontend/index.js index 520274cbf77b349020e100a19f5922442c751531..1faad005dfbbe16d7f2190fd2c921721a5e7ed6a 100644 --- a/aleksis/apps/tezor/frontend/index.js +++ b/aleksis/apps/tezor/frontend/index.js @@ -1,5 +1,3 @@ -import { hasPersonValidator } from "aleksis.core/routeValidators"; - export default { meta: { inMenu: true, @@ -131,5 +129,5 @@ export default { }, name: "tezor.sendInvoiceByToken", }, - ], + ], }; diff --git a/aleksis/apps/tezor/locale/ar/LC_MESSAGES/django.po b/aleksis/apps/tezor/locale/ar/LC_MESSAGES/django.po index b1957a3b565b5394872ec321a986140c4b868b20..056ca67ca1155f5c350485e7d38fc1ed295d4ba5 100644 --- a/aleksis/apps/tezor/locale/ar/LC_MESSAGES/django.po +++ b/aleksis/apps/tezor/locale/ar/LC_MESSAGES/django.po @@ -337,7 +337,7 @@ msgid "Pay now" msgstr "" #: aleksis/apps/tezor/templates/tezor/invoice/full.html:113 -msgid "Mark as payed" +msgid "Mark as paid" msgstr "" #: aleksis/apps/tezor/templates/tezor/invoice/list.html:6 diff --git a/aleksis/apps/tezor/locale/de_DE/LC_MESSAGES/django.po b/aleksis/apps/tezor/locale/de_DE/LC_MESSAGES/django.po index 5c4d8f62cf804bcf0575c10f4b892696c5c7bad8..eba261761da6ad15cf9fce484ad3062301aa953b 100644 --- a/aleksis/apps/tezor/locale/de_DE/LC_MESSAGES/django.po +++ b/aleksis/apps/tezor/locale/de_DE/LC_MESSAGES/django.po @@ -365,7 +365,7 @@ msgid "Pay now" msgstr "Jetzt zahlen" #: aleksis/apps/tezor/templates/tezor/invoice/full.html:113 -msgid "Mark as payed" +msgid "Mark as paid" msgstr "" #: aleksis/apps/tezor/templates/tezor/invoice/list.html:6 diff --git a/aleksis/apps/tezor/locale/fr/LC_MESSAGES/django.po b/aleksis/apps/tezor/locale/fr/LC_MESSAGES/django.po index 48ffaaccd554a496b33d5670aa6de26ad167ec0c..1078b7977ad3a2ca10b0a46660e4a15241ee9ef3 100644 --- a/aleksis/apps/tezor/locale/fr/LC_MESSAGES/django.po +++ b/aleksis/apps/tezor/locale/fr/LC_MESSAGES/django.po @@ -337,7 +337,7 @@ msgid "Pay now" msgstr "" #: aleksis/apps/tezor/templates/tezor/invoice/full.html:113 -msgid "Mark as payed" +msgid "Mark as paid" msgstr "" #: aleksis/apps/tezor/templates/tezor/invoice/list.html:6 diff --git a/aleksis/apps/tezor/locale/la/LC_MESSAGES/django.po b/aleksis/apps/tezor/locale/la/LC_MESSAGES/django.po index c5273a35b09c009825f37040d55cbef6fd51e517..889dce8605d2a7c1e9b1e853fa13462e1144c08a 100644 --- a/aleksis/apps/tezor/locale/la/LC_MESSAGES/django.po +++ b/aleksis/apps/tezor/locale/la/LC_MESSAGES/django.po @@ -336,7 +336,7 @@ msgid "Pay now" msgstr "" #: aleksis/apps/tezor/templates/tezor/invoice/full.html:113 -msgid "Mark as payed" +msgid "Mark as paid" msgstr "" #: aleksis/apps/tezor/templates/tezor/invoice/list.html:6 diff --git a/aleksis/apps/tezor/locale/nb_NO/LC_MESSAGES/django.po b/aleksis/apps/tezor/locale/nb_NO/LC_MESSAGES/django.po index c5273a35b09c009825f37040d55cbef6fd51e517..889dce8605d2a7c1e9b1e853fa13462e1144c08a 100644 --- a/aleksis/apps/tezor/locale/nb_NO/LC_MESSAGES/django.po +++ b/aleksis/apps/tezor/locale/nb_NO/LC_MESSAGES/django.po @@ -336,7 +336,7 @@ msgid "Pay now" msgstr "" #: aleksis/apps/tezor/templates/tezor/invoice/full.html:113 -msgid "Mark as payed" +msgid "Mark as paid" msgstr "" #: aleksis/apps/tezor/templates/tezor/invoice/list.html:6 diff --git a/aleksis/apps/tezor/locale/tr_TR/LC_MESSAGES/django.po b/aleksis/apps/tezor/locale/tr_TR/LC_MESSAGES/django.po index c5273a35b09c009825f37040d55cbef6fd51e517..889dce8605d2a7c1e9b1e853fa13462e1144c08a 100644 --- a/aleksis/apps/tezor/locale/tr_TR/LC_MESSAGES/django.po +++ b/aleksis/apps/tezor/locale/tr_TR/LC_MESSAGES/django.po @@ -336,7 +336,7 @@ msgid "Pay now" msgstr "" #: aleksis/apps/tezor/templates/tezor/invoice/full.html:113 -msgid "Mark as payed" +msgid "Mark as paid" msgstr "" #: aleksis/apps/tezor/templates/tezor/invoice/list.html:6 diff --git a/aleksis/apps/tezor/migrations/0001_initial.py b/aleksis/apps/tezor/migrations/0001_initial.py index 98802cabe1a043e935049e80df04c07b1ce497e7..78902e2c22002582ad57b32b646b851d9f328a94 100644 --- a/aleksis/apps/tezor/migrations/0001_initial.py +++ b/aleksis/apps/tezor/migrations/0001_initial.py @@ -1,7 +1,7 @@ # Generated by Django 3.2.12 on 2022-03-06 21:33 import aleksis.core.mixins -import django.contrib.sites.managers +import aleksis.core.managers from django.db import migrations, models import django.db.models.deletion @@ -25,7 +25,7 @@ class Migration(migrations.Migration): ('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.site')), ], managers=[ - ('objects', django.contrib.sites.managers.CurrentSiteManager()), + ('objects', aleksis.core.managers.AlekSISBaseManager()), ], ), migrations.CreateModel( @@ -39,7 +39,7 @@ class Migration(migrations.Migration): ('site', models.ForeignKey(default=1, editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.site')), ], managers=[ - ('objects', django.contrib.sites.managers.CurrentSiteManager()), + ('objects', aleksis.core.managers.AlekSISBaseManager()), ], ), migrations.CreateModel( diff --git a/aleksis/apps/tezor/migrations/0003_manual_invoicing.py b/aleksis/apps/tezor/migrations/0003_manual_invoicing.py index 90b7100ced2bcb58c6016004b88dcc38d15e5ee9..125bae2603ba33764d4dcb3c245d92dc06a2255e 100644 --- a/aleksis/apps/tezor/migrations/0003_manual_invoicing.py +++ b/aleksis/apps/tezor/migrations/0003_manual_invoicing.py @@ -1,6 +1,6 @@ # Generated by Django 3.2.12 on 2022-03-12 21:41 -import django.contrib.sites.managers +import aleksis.core.managers from django.db import migrations, models import django.db.models.deletion @@ -29,7 +29,7 @@ class Migration(migrations.Migration): 'abstract': False, }, managers=[ - ('objects', django.contrib.sites.managers.CurrentSiteManager()), + ('objects', aleksis.core.managers.AlekSISBaseManager()), ], ), migrations.AddField( diff --git a/aleksis/apps/tezor/migrations/0007_client_payment_variants.py b/aleksis/apps/tezor/migrations/0007_client_payment_variants.py index 26e258ef53333dc9644b69f289b2d54f6947e9a1..92b9380215f1a1fa285e615ebe39efde0c9a353d 100644 --- a/aleksis/apps/tezor/migrations/0007_client_payment_variants.py +++ b/aleksis/apps/tezor/migrations/0007_client_payment_variants.py @@ -39,7 +39,7 @@ def configure_clients(apps, schema_editor): values[f"{variant}_enabled"] = False warnings.warn(f"Payment variant {variant} enabled but {field.name} not configured!") - Client.objects.update(**values) + Client._base_manager.update(**values) class Migration(migrations.Migration): diff --git a/aleksis/apps/tezor/migrations/0010_alter_client_options_alter_invoice_options_and_more.py b/aleksis/apps/tezor/migrations/0010_alter_client_options_alter_invoice_options_and_more.py new file mode 100644 index 0000000000000000000000000000000000000000..45e0a284018bdca6cfcd10fea7e791ba6dc6aa7c --- /dev/null +++ b/aleksis/apps/tezor/migrations/0010_alter_client_options_alter_invoice_options_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 4.2.3 on 2023-07-22 15:41 + +import aleksis.core.managers +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ('tezor', '0009_invoice_billing_phone'), + ] + + operations = [ + migrations.AddField( + model_name='client', + name='managed_by_app_label', + field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'), + ), + migrations.AddField( + model_name='invoicegroup', + name='managed_by_app_label', + field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'), + ), + migrations.AddField( + model_name='invoiceitem', + name='managed_by_app_label', + field=models.CharField(blank=True, editable=False, max_length=255, verbose_name='App label of app responsible for managing this instance'), + ), + ] diff --git a/aleksis/apps/tezor/models/base.py b/aleksis/apps/tezor/models/base.py index d44902ea81627a68c07197a309bd8592c6800dc0..a2d27723c65e72b133157d6fd4e735bf825a55ca 100644 --- a/aleksis/apps/tezor/models/base.py +++ b/aleksis/apps/tezor/models/base.py @@ -54,6 +54,8 @@ class Client(ExtensibleModel): pledge_enabled = models.BooleanField(verbose_name=_("Pledge enabled"), default=False) class Meta: + verbose_name = _("Client") + verbose_name_plural = _("Clients") constraints = [ models.UniqueConstraint(fields=["name", "site"], name="uniq_client_per_site"), models.CheckConstraint( diff --git a/aleksis/apps/tezor/models/invoice.py b/aleksis/apps/tezor/models/invoice.py index eda458c5919c003c630fc5a517a3dc85d567b964..408a39512a2053024233bc7cf7b049428d5a573a 100644 --- a/aleksis/apps/tezor/models/invoice.py +++ b/aleksis/apps/tezor/models/invoice.py @@ -28,6 +28,13 @@ class InvoiceGroup(ExtensibleModel): verbose_name=_("Template to render invoices with as PDF"), blank=True, max_length=255 ) + class Meta: + verbose_name = _("Invoice Group") + verbose_name_plural = _("Invoice Groups") + constraints = [ + models.UniqueConstraint(fields=["client", "name"], name="group_uniq_per_client") + ] + def __str__(self) -> str: return self.name @@ -37,11 +44,6 @@ class InvoiceGroup(ExtensibleModel): else: return Client.get_variant_choices() - class Meta: - constraints = [ - models.UniqueConstraint(fields=["client", "name"], name="group_uniq_per_client") - ] - class Invoice(BasePayment, PureDjangoModel): STATUS_ICONS = { @@ -81,6 +83,17 @@ class Invoice(BasePayment, PureDjangoModel): ) items = models.ManyToManyField("InvoiceItem", verbose_name=_("Invoice items")) + class Meta: + verbose_name = _("Invoice") + verbose_name_plural = _("Invoices") + constraints = [ + models.UniqueConstraint(fields=["number", "group"], name="number_uniq_per_group"), + ] + permissions = (("send_invoice_email", _("Can send invoice by email")),) + + def __str__(self): + return self.number + def save(self, *args, **kwargs): if self.person: person = self.person @@ -104,6 +117,9 @@ class Invoice(BasePayment, PureDjangoModel): super().save(*args, **kwargs) + def get_absolute_url(self): + return reverse("invoice_by_token", kwargs={"slug": self.token}) + def get_variant_choices(self=None): if self and self.group: return self.group.get_variant_choices() @@ -137,12 +153,6 @@ class Invoice(BasePayment, PureDjangoModel): return None - class Meta: - constraints = [ - models.UniqueConstraint(fields=["number", "group"], name="number_uniq_per_group"), - ] - permissions = (("send_invoice_email", _("Can send invoice by email")),) - def get_billing_email_recipients(self): if hasattr(self.for_object, "get_billing_email_recipients"): return self.for_object.get_billing_email_recipients() @@ -182,9 +192,6 @@ class Invoice(BasePayment, PureDjangoModel): return TotalsTable(values) - def get_absolute_url(self): - return reverse("invoice_by_token", kwargs={"slug": self.token}) - def get_success_url(self): return self.get_absolute_url() @@ -203,6 +210,13 @@ class InvoiceItem(ExtensibleModel): verbose_name=_("Tax rate"), max_digits=4, decimal_places=1, default="0.0" ) + class Meta: + verbose_name = _("Invoice Item") + verbose_name_plural = _("Invoice Items") + + def __str__(self): + return f"{self.sku}: {self.description}" + def as_purchased_item(self): return PurchasedItem( name=self.description, diff --git a/aleksis/apps/tezor/rules.py b/aleksis/apps/tezor/rules.py index d595e53b56073a0096341d1d98551a387dbb2f7f..53ab4a7d3d37cca3dcdbc5f017337abbe1f7975c 100644 --- a/aleksis/apps/tezor/rules.py +++ b/aleksis/apps/tezor/rules.py @@ -154,5 +154,7 @@ rules.add_perm("tezor.send_invoice_email_rule", send_invoice_email_predicate) view_own_invoices_predicate = has_person rules.add_perm("tezor.view_own_invoices_list_rule", view_own_invoices_predicate) -view_menu_predicate = view_own_invoices_predicate | view_clients_predicate | view_invoice_groups_predicate +view_menu_predicate = ( + view_own_invoices_predicate | view_clients_predicate | view_invoice_groups_predicate +) rules.add_perm("tezor.view_menu_rule", view_menu_predicate) diff --git a/aleksis/apps/tezor/tables.py b/aleksis/apps/tezor/tables.py index 187a5a9fd4a0e73c6a99e823d9e08fbdd40432f9..10227b1c90e997cbcd376cfcadb45ba025f3935c 100644 --- a/aleksis/apps/tezor/tables.py +++ b/aleksis/apps/tezor/tables.py @@ -102,7 +102,7 @@ class InvoicesTable(tables.Table): verbose_name=_("View"), text=_("View"), ) - print = tables.LinkColumn( + print_action = tables.LinkColumn( "print_invoice", args=[A("token")], verbose_name=_("Print"), diff --git a/aleksis/apps/tezor/templates/tezor/invoice/full.html b/aleksis/apps/tezor/templates/tezor/invoice/full.html index 2d20d990cc57f56b3062db772b68766f64dfb71b..16cc9ad74654cf2174e5002bb6f32c4939f1b11b 100644 --- a/aleksis/apps/tezor/templates/tezor/invoice/full.html +++ b/aleksis/apps/tezor/templates/tezor/invoice/full.html @@ -108,9 +108,9 @@ {% endif %} {% if object.status == "preauth" %} <div class="card-action"> - <a class="btn waves-effect waves-light green" href="{% url 'mark_invoice_payed_by_token' object.token %}"> + <a class="btn waves-effect waves-light green" href="{% url 'mark_invoice_paid_by_token' object.token %}"> <i class="material-icons left iconify" data-icon="mdi:check-all"></i> - {% trans "Mark as payed" %} + {% trans "Mark as paid" %} </a> </div> {% endif %} diff --git a/aleksis/apps/tezor/urls.py b/aleksis/apps/tezor/urls.py index cb883f1e2b09b3ff6a4d65185ea741d5ec346ec9..6b327c8a407805cd21f5fac5eb391fb1423f2861 100644 --- a/aleksis/apps/tezor/urls.py +++ b/aleksis/apps/tezor/urls.py @@ -64,8 +64,8 @@ urlpatterns = [ name="send_invoice_by_token", ), path( - "invoice/<str:token>/mark_payed/", + "invoice/<str:token>/mark_paid/", views.MarkPayedView.as_view(), - name="mark_invoice_payed_by_token", + name="mark_invoice_paid_by_token", ), ] diff --git a/aleksis/apps/tezor/views.py b/aleksis/apps/tezor/views.py index fc21bea109390410916934ead624940076f4983b..b494fca37b314b0f4f7affa7dbe2f22b1001a170 100644 --- a/aleksis/apps/tezor/views.py +++ b/aleksis/apps/tezor/views.py @@ -282,7 +282,7 @@ class MyInvoicesListView(PermissionRequiredMixin, SingleTableView): class MarkPayedView(PermissionRequiredMixin, View): model = Invoice - permission_required = "tezor.mark_payed_rule" + permission_required = "tezor.mark_paid_rule" template_name = "tezor/invoice/full.html" diff --git a/pyproject.toml b/pyproject.toml index bc6fb80efb2d0aeb43883ee274c5e6dd2bb4b0e0..c3c19671c6c43a12fc61d987162859cfb13dcfa7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,24 +22,67 @@ classifiers = [ "Intended Audience :: Education", "Topic :: Education" ] +maintainers = ["Jonathan Weth <dev@jonathanweth.de>", "Dominik George <dominik.george@teckids.org>"] + +[[tool.poetry.source]] +name = "PyPI" +priority = "primary" [[tool.poetry.source]] name = "gitlab" url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple" -secondary = true - +priority = "supplemental" [tool.poetry.dependencies] python = "^3.9" django-payments-sepa = { version = "^1.2.dev0", allow-prereleases = true, extras = ["fints"] } -aleksis-core = "^3.0" +aleksis-core = "^4.0.0.dev0" django-payments = { version = "^1.0.0", extras = ["sofort"] } -[tool.poetry.dev-dependencies] -aleksis-builddeps = "*" - [tool.poetry.plugins."aleksis.app"] tezor = "aleksis.apps.tezor.apps:DefaultConfig" + +[tool.poetry.group.dev.dependencies] +django-stubs = "^4.2" + +safety = "^2.3.5" + +flake8 = "^6.0.0" +flake8-django = "^1.0.0" +flake8-fixme = "^1.1.1" +flake8-mypy = "^17.8.0" +flake8-bandit = "^4.1.1" +flake8-builtins = "^2.0.0" +flake8-docstrings = "^1.5.0" +flake8-rst-docstrings = "^0.3.0" + +black = ">=21.0" +flake8-black = "^0.3.0" + +isort = "^5.0.0" +flake8-isort = "^6.0.0" + +curlylint = "^0.13.0" + +[tool.poetry.group.test.dependencies] +pytest = "^7.2" +pytest-django = "^4.1" +pytest-django-testing-postgresql = "^0.2" +pytest-cov = "^4.0.0" +pytest-sugar = "^0.9.2" +selenium = "<4.10.0" +freezegun = "^1.1.0" + +[tool.poetry.group.docs] +optional = true + +[tool.poetry.group.docs.dependencies] +sphinx = "^7.0" +sphinxcontrib-django = "^2.3.0" +sphinxcontrib-svg2pdfconverter = "^1.1.1" +sphinx-autodoc-typehints = "^1.7" +sphinx_material = "^0.0.35" + [tool.black] line-length = 100 exclude = "/migrations/" @@ -57,5 +100,5 @@ no_autofocus = true tabindex_no_positive = true [build-system] -requires = ["poetry>=1.0"] -build-backend = "poetry.masonry.api" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/tox.ini b/tox.ini index 6e4b77ab1ded935257117696975c2150772cd85c..85c2494a5a2f5480bb05d48781edfaa74803eeab 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ skip_missing_interpreters = true envlist = py39,py310,py311 [testenv] -whitelist_externals = poetry +allowlist_externals = poetry skip_install = true envdir = {toxworkdir}/globalenv commands_pre = @@ -68,18 +68,6 @@ known_django = django skip = migrations sections = FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER -[mypy] -plugins = mypy_django_plugin.main -python_version = 3.8 -platform = linux -show_column_numbers = True -follow_imports = skip -ignore_missing_imports = True -cache_dir = /dev/null - -[mypy.plugins.django-stubs] -django_settings_module = aleksis.core.settings - [pytest] DJANGO_SETTINGS_MODULE = aleksis.core.settings junit_family = legacy