From c992fed3e66036e112bca9d4566b79f3db38feee Mon Sep 17 00:00:00 2001
From: Paul J Stevens
Date: Sat, 13 Jan 2024 21:36:20 +0100
Subject: [PATCH] refactoring
---
.pre-commit-config.yaml | 14 +++++
Makefile | 6 +-
etc/supervisord.conf | 2 +-
pdm.lock | 128 +++++++++++++++++++++++++++++++++++++++-
pyproject.toml | 4 +-
src/bij1/erp/config.py | 65 ++++++++++++++++++++
src/bij1/erp/models.py | 52 ++++++++--------
7 files changed, 239 insertions(+), 32 deletions(-)
create mode 100644 .pre-commit-config.yaml
create mode 100644 src/bij1/erp/config.py
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..14316d8
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,14 @@
+# File format: https://pre-commit.com/#plugins
+# Supported hooks: https://pre-commit.com/hooks.html
+# Running "make format" fixes most issues for you
+repos:
+ - repo: https://github.com/ambv/black
+ rev: 23.12.1
+ hooks:
+ - id: black
+ exclude: ^.*\b(migrations)\b.*$
+ - repo: https://github.com/charliermarsh/ruff-pre-commit
+ # Ruff version.
+ rev: "v0.1.13"
+ hooks:
+ - id: ruff
diff --git a/Makefile b/Makefile
index f440220..dce697d 100644
--- a/Makefile
+++ b/Makefile
@@ -6,6 +6,7 @@ DBNAME=bij1
install:
pdm install -d
+ pre-commit install
setup:
TRYTOND_DATABASE_URI=postgresql://localhost/$(DBNAME) trytond-admin -d $(DBNAME) --all --verbose
@@ -13,8 +14,11 @@ setup:
run:
supervisord
+configure:
+ bin/bij1_setup
+
sync:
- bin/airtable -c etc/dev.ini -d $(DBNAME)
+ bin/bij1_airtable
frontend: frontend/package.json
make -C frontend
diff --git a/etc/supervisord.conf b/etc/supervisord.conf
index 48a978e..1ce5666 100644
--- a/etc/supervisord.conf
+++ b/etc/supervisord.conf
@@ -24,7 +24,7 @@ stopasgroup=true
redirect_stderr=true
[program:api]
-command=uvicorn bij1.api.main:app --reload --port 5000
+command=uvicorn bij1.api.main:app --reload-dir=%(ENV_PWD)s/src/ --port 5000
stopasgroup=true
redirect_stderr=true
diff --git a/pdm.lock b/pdm.lock
index 178960a..0b888f3 100644
--- a/pdm.lock
+++ b/pdm.lock
@@ -5,7 +5,7 @@
groups = ["default", "dev"]
strategy = ["cross_platform", "inherit_metadata"]
lock_version = "4.4.1"
-content_hash = "sha256:7505f6c4957e308846e2f5aa7d93c6569ae440f1e934f4e370578ead99fce95a"
+content_hash = "sha256:0a53b25a34df010604c4713ea2e930d7689fdf1959140aeefd5550c0a9511c4e"
[[package]]
name = "annotated-types"
@@ -44,6 +44,17 @@ files = [
{file = "certifi-2023.11.17.tar.gz", hash = "sha256:9b469f3a900bf28dc19b8cfbf8019bf47f7fdd1a65a1d4ffb98fc14166beb4d1"},
]
+[[package]]
+name = "cfgv"
+version = "3.4.0"
+requires_python = ">=3.8"
+summary = "Validate configuration and produce human readable error messages."
+groups = ["default"]
+files = [
+ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
+ {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
+]
+
[[package]]
name = "charset-normalizer"
version = "3.3.2"
@@ -122,6 +133,16 @@ files = [
{file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
]
+[[package]]
+name = "distlib"
+version = "0.3.8"
+summary = "Distribution utilities"
+groups = ["default"]
+files = [
+ {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"},
+ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"},
+]
+
[[package]]
name = "factory-boy"
version = "3.3.0"
@@ -181,6 +202,17 @@ files = [
{file = "fastapi-0.108.0.tar.gz", hash = "sha256:5056e504ac6395bf68493d71fcfc5352fdbd5fda6f88c21f6420d80d81163296"},
]
+[[package]]
+name = "filelock"
+version = "3.13.1"
+requires_python = ">=3.8"
+summary = "A platform independent file lock."
+groups = ["default"]
+files = [
+ {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"},
+ {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"},
+]
+
[[package]]
name = "genshi"
version = "0.7.7"
@@ -219,6 +251,17 @@ files = [
{file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"},
]
+[[package]]
+name = "identify"
+version = "2.5.33"
+requires_python = ">=3.8"
+summary = "File identification library for Python"
+groups = ["default"]
+files = [
+ {file = "identify-2.5.33-py2.py3-none-any.whl", hash = "sha256:d40ce5fcd762817627670da8a7d8d8e65f24342d14539c59488dc603bf662e34"},
+ {file = "identify-2.5.33.tar.gz", hash = "sha256:161558f9fe4559e1557e1bff323e8631f6a0e4837f7497767c1782832f16b62d"},
+]
+
[[package]]
name = "idna"
version = "3.6"
@@ -347,6 +390,20 @@ files = [
{file = "marshmallow-3.20.1.tar.gz", hash = "sha256:5d2371bbe42000f2b3fb5eaa065224df7d8f8597bc19a1bbfa5bfe7fba8da889"},
]
+[[package]]
+name = "nodeenv"
+version = "1.8.0"
+requires_python = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*"
+summary = "Node.js virtual environment builder"
+groups = ["default"]
+dependencies = [
+ "setuptools",
+]
+files = [
+ {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"},
+ {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"},
+]
+
[[package]]
name = "packaging"
version = "23.2"
@@ -368,6 +425,17 @@ files = [
{file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"},
]
+[[package]]
+name = "platformdirs"
+version = "4.1.0"
+requires_python = ">=3.8"
+summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+groups = ["default"]
+files = [
+ {file = "platformdirs-4.1.0-py3-none-any.whl", hash = "sha256:11c8f37bcca40db96d8144522d925583bdb7a31f7b0e37e3ed4318400a8e2380"},
+ {file = "platformdirs-4.1.0.tar.gz", hash = "sha256:906d548203468492d432bcb294d4bc2fff751bf84971fbb2c10918cc206ee420"},
+]
+
[[package]]
name = "pluggy"
version = "1.3.0"
@@ -389,6 +457,24 @@ files = [
{file = "polib-1.2.0.tar.gz", hash = "sha256:f3ef94aefed6e183e342a8a269ae1fc4742ba193186ad76f175938621dbfc26b"},
]
+[[package]]
+name = "pre-commit"
+version = "3.6.0"
+requires_python = ">=3.9"
+summary = "A framework for managing and maintaining multi-language pre-commit hooks."
+groups = ["default"]
+dependencies = [
+ "cfgv>=2.0.0",
+ "identify>=1.0.0",
+ "nodeenv>=0.11.1",
+ "pyyaml>=5.1",
+ "virtualenv>=20.10.0",
+]
+files = [
+ {file = "pre_commit-3.6.0-py2.py3-none-any.whl", hash = "sha256:c255039ef399049a5544b6ce13d135caba8f2c28c3b4033277a788f434308376"},
+ {file = "pre_commit-3.6.0.tar.gz", hash = "sha256:d30bad9abf165f7785c15a21a1f46da7d0677cb00ee7ff4c579fd38922efe15d"},
+]
+
[[package]]
name = "proteus"
version = "6.8.1"
@@ -609,6 +695,30 @@ files = [
{file = "python_stdnum-1.19-py2.py3-none-any.whl", hash = "sha256:1b5b401ad3f45b798b0317313b781a433f5d7a5ff2c9feb8054664f76f78644e"},
]
+[[package]]
+name = "pyyaml"
+version = "6.0.1"
+requires_python = ">=3.6"
+summary = "YAML parser and emitter for Python"
+groups = ["default"]
+files = [
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"},
+ {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"},
+ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"},
+ {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"},
+ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"},
+ {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"},
+ {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"},
+ {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"},
+ {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"},
+ {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"},
+ {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"},
+ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
+]
+
[[package]]
name = "relatorio"
version = "0.10.1"
@@ -1180,6 +1290,22 @@ files = [
{file = "uvicorn-0.25.0.tar.gz", hash = "sha256:6dddbad1d7ee0f5140aba5ec138ddc9612c5109399903828b4874c9937f009c2"},
]
+[[package]]
+name = "virtualenv"
+version = "20.25.0"
+requires_python = ">=3.7"
+summary = "Virtual Python Environment builder"
+groups = ["default"]
+dependencies = [
+ "distlib<1,>=0.3.7",
+ "filelock<4,>=3.12.2",
+ "platformdirs<5,>=3.9.1",
+]
+files = [
+ {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"},
+ {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"},
+]
+
[[package]]
name = "werkzeug"
version = "3.0.1"
diff --git a/pyproject.toml b/pyproject.toml
index 6258a99..b1448f5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -24,13 +24,15 @@ dependencies = [
"uvicorn>=0.25.0",
"gunicorn>=21.2.0",
"supervisor>=4.2.5",
+ "pre-commit>=3.6.0",
]
requires-python = ">=3.11"
readme = "README.md"
license = {text = "Proprietary"}
[project.scripts]
-airtable = "bij1.erp.airtable:airtable"
+bij1_airtable = "bij1.erp.airtable:airtable"
+bij1_setup = "bij1.erp.config:configure"
[tool.pdm]
package-dir = "src"
diff --git a/src/bij1/erp/config.py b/src/bij1/erp/config.py
new file mode 100644
index 0000000..743c0a8
--- /dev/null
+++ b/src/bij1/erp/config.py
@@ -0,0 +1,65 @@
+from decimal import Decimal
+from typing import Any
+from trytond.transaction import Transaction
+from bij1.erp import pool
+
+
+__all__ = ["config"]
+
+COMPANY_NAME = "BIJ1"
+CURRENCY_CODE = "EUR"
+CURRENCY_SYMBOL = "€"
+SUBSCRIPTION_DESCRIPTION = f"Lidmaatschap {COMPANY_NAME}"
+
+
+class CONFIG:
+ company: Any
+ currency: Any
+ subscription_description: str = SUBSCRIPTION_DESCRIPTION
+
+ def __init__(self):
+ with Transaction().start(pool.database_name, 0):
+ self._company()
+ self._currency()
+
+ def _party(self):
+ Party = pool.get("party.party")
+ if not (party := Party.search([("name", "=", COMPANY_NAME)])):
+ party = Party()
+ party.name = COMPANY_NAME
+ party.save()
+ else:
+ party = party[0]
+ return party
+
+ def _company(self):
+ Company = pool.get("company.company")
+ if not (company := Company.search([("party.name", "=", COMPANY_NAME)])):
+ company = Company()
+ company.party = self._party()
+ company.save()
+ else:
+ company = company[0]
+
+ self.company = company
+
+ def _currency(self):
+ Currency = pool.get("currency.currency")
+ if not (currency := Currency.search([("code", "=", "EUR")])):
+ currency = Currency()
+ currency.rounding = Decimal("0.01")
+ currency.digits = 2
+ else:
+ currency = currency[0]
+
+ currency.code = CURRENCY_CODE
+ currency.symbol = CURRENCY_SYMBOL
+ currency.save()
+ self.currency = currency
+
+
+config = CONFIG()
+
+
+def configure():
+ pass
diff --git a/src/bij1/erp/models.py b/src/bij1/erp/models.py
index 0f50195..81dd65c 100644
--- a/src/bij1/erp/models.py
+++ b/src/bij1/erp/models.py
@@ -5,6 +5,7 @@ from decimal import Decimal
import typing
import logging
from bij1.erp import pool
+from bij1.erp.config import config
logger = logging.getLogger(__name__)
@@ -42,8 +43,6 @@ class Member:
def save_party(self):
Party = pool.get("party.party")
- Company = pool.get("company.company")
- self.company = Company.search([])[0]
if not (obj := Party.search([("name", "=", self.name)])):
obj = Party()
obj.name = self.name
@@ -61,6 +60,7 @@ class Member:
address.street = self.address
address.postal_code = self.postcode
address.city = self.city
+ address.invoice = True
address.save()
def save_email(self):
@@ -150,7 +150,7 @@ class Member:
if not (self.iban and self.mandate):
return
Mandate = pool.get("account.payment.sepa.mandate")
- if Mandate.search([('identification', '=', self.mandate)]):
+ if Mandate.search([("identification", "=", self.mandate)]):
return
BankAccount = pool.get("bank.account")
@@ -167,7 +167,7 @@ class Member:
account_number = bank_account.numbers[0]
mandate = Mandate()
mandate.identification = self.mandate
- mandate.company = self.company
+ mandate.company = config.company
mandate.account_number = account_number
mandate.party = self.party
mandate.signature_date = self.since
@@ -176,45 +176,41 @@ class Member:
def save_subscription(self):
if not self.period:
return
- Currency = pool.get('currency.currency')
- currency, = Currency.search([('code', '=', 'EUR')])
- Subscription = pool.get('sale.subscription')
- SubscriptionLine = pool.get('sale.subscription.line')
- SubscriptionService = pool.get('sale.subscription.service')
- services = SubscriptionService.search([('active', '=', True)])
+ Subscription = pool.get("sale.subscription")
+ SubscriptionLine = pool.get("sale.subscription.line")
+ SubscriptionService = pool.get("sale.subscription.service")
+ services = SubscriptionService.search([("active", "=", True)])
services = {x.consumption_recurrence.name: x for x in services}
- if not (subscription := Subscription.search(['party', '=', self.party])):
+ if not (service := services.get(self.period.lower())):
+ raise Exception("unkown period: %s" % self.period)
+
+ if not (subscription := Subscription.search(["party", "=", self.party])):
subscription = Subscription()
- subscription.company = self.company
+ subscription.company = config.company
subscription.party = self.party
- subscription.currency = currency
+ subscription.currency = config.currency
subscription.start_date = self.since
- subscription.invoice_start_date = date(2024,1,1)
+ subscription.invoice_start_date = date(2024, 1, 1)
+ subscription.invoice_address = self.party.addresses[0]
+ subscription.invoice_recurrence = service.consumption_recurrence
else:
subscription = subscription[0]
- subscription.description = "Lidmaatschap BIJ1"
- if not subscription.invoice_address:
- subscription.invoice_address = self.party.addresses[0]
-
- service = services.get(self.period.lower())
- if not service:
- raise Exception("unkown period: %s" % self.period)
-
- subscription.invoice_recurrence = service.consumption_recurrence
+ subscription.description = config.subscription_description
subscription.save()
- if not (line := SubscriptionLine.search([
- ('subscription', '=', subscription),
- ('service', '=', service)
- ])):
+ if not (
+ line := SubscriptionLine.search(
+ [("subscription", "=", subscription), ("service", "=", service)]
+ )
+ ):
line = SubscriptionLine()
line.service = service
line.subscription = subscription
line.quantity = 1
line.unit_price = self.amount
- line.start_date = max([self.since, date(2024,1,1)])
+ line.start_date = max([self.since, date(2024, 1, 1)])
line.unit = service.product.default_uom
line.save()