diff --git a/backend/api/urls.py b/backend/api/urls.py index c123c2ff6..af263e4b1 100644 --- a/backend/api/urls.py +++ b/backend/api/urls.py @@ -9,27 +9,30 @@ path("clients/", include("backend.clients.api.urls", namespace="clients")), path("", include("backend.finance.api.urls", namespace="finance")), path( - "settings/", - include( - ( - [ - path("client_defaults//", handle_client_defaults_endpoints, name="client_defaults"), - path("client_defaults/", handle_client_defaults_endpoints, name="client_defaults without client"), - path( - "client_defaults/remove_default_logo/", - remove_client_default_logo_endpoint, - name="client_defaults remove logo without client", - ), - path( - "client_defaults/remove_default_logo/", - remove_client_default_logo_endpoint, - name="client_defaults remove logo", - ), - ], - "settings", - ), - namespace="settings", - ), + "", + include("backend.api.settings.urls", namespace="settings"), + # path( + # "settings/", + # include( + # ( + # [ + # path("client_defaults//", handle_client_defaults_endpoints, name="client_defaults"), + # path("client_defaults/", handle_client_defaults_endpoints, name="client_defaults without client"), + # path( + # "client_defaults/remove_default_logo/", + # remove_client_default_logo_endpoint, + # name="client_defaults remove logo without client", + # ), + # path( + # "client_defaults/remove_default_logo/", + # remove_client_default_logo_endpoint, + # name="client_defaults remove logo", + # ), + # ], + # "settings", + # ), + # namespace="settings", + # ), ), path("public/", include("backend.api.public.urls")), path("emails/", include("backend.api.emails.urls")), diff --git a/backend/core/service/settings/view/email_templates.py b/backend/core/service/settings/view/email_templates.py new file mode 100644 index 000000000..22d5639a6 --- /dev/null +++ b/backend/core/service/settings/view/email_templates.py @@ -0,0 +1,20 @@ +from core.types.requests import WebRequest + +from backend.finance.service.defaults.get import get_account_defaults + + +def email_templates_context(request: WebRequest, context: dict) -> None: + acc_defaults = get_account_defaults(request.actor) + context.update( + { + "account_defaults": acc_defaults, + "email_templates": { + "recurring_invoices": { + "invoice_created": acc_defaults.email_template_recurring_invoices_invoice_created, + "invoice_overdue": acc_defaults.email_template_recurring_invoices_invoice_overdue, + "invoice_cancelled": acc_defaults.email_template_recurring_invoices_invoice_cancelled, + } + }, + } + ) + # print(context.get("email_templates")) diff --git a/frontend/templates/pages/settings/pages/email_templates.html b/frontend/templates/core/settings/pages/email_templates.html similarity index 100% rename from frontend/templates/pages/settings/pages/email_templates.html rename to frontend/templates/core/settings/pages/email_templates.html diff --git a/poetry.lock b/poetry.lock index 134366d12..87a453135 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1828,13 +1828,13 @@ lxml = ["lxml"] [[package]] name = "identify" -version = "2.6.3" +version = "2.6.4" description = "File identification library for Python" optional = false python-versions = ">=3.9" files = [ - {file = "identify-2.6.3-py2.py3-none-any.whl", hash = "sha256:9edba65473324c2ea9684b1f944fe3191db3345e50b6d04571d10ed164f8d7bd"}, - {file = "identify-2.6.3.tar.gz", hash = "sha256:62f5dae9b5fef52c84cc188514e9ea4f3f636b1d8799ab5ebc475471f9e47a02"}, + {file = "identify-2.6.4-py2.py3-none-any.whl", hash = "sha256:993b0f01b97e0568c179bb9196391ff391bfb88a99099dbf5ce392b68f42d0af"}, + {file = "identify-2.6.4.tar.gz", hash = "sha256:285a7d27e397652e8cafe537a6cc97dd470a970f48fb2e9d979aa38eae5513ac"}, ] [package.extras] @@ -2728,13 +2728,13 @@ virtualenv = ">=20.10.0" [[package]] name = "prompt-toolkit" -version = "3.0.36" +version = "3.0.48" description = "Library for building powerful interactive command lines in Python" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.36-py3-none-any.whl", hash = "sha256:aa64ad242a462c5ff0363a7b9cfe696c20d55d9fc60c11fd8e632d064804d305"}, - {file = "prompt_toolkit-3.0.36.tar.gz", hash = "sha256:3e163f254bef5a03b146397d7c1963bd3e2812f0964bb9a24e6ec761fd28db63"}, + {file = "prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e"}, + {file = "prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90"}, ] [package.dependencies] @@ -3419,17 +3419,17 @@ png = ["pypng"] [[package]] name = "questionary" -version = "2.0.1" +version = "2.1.0" description = "Python library to build pretty command line user prompts ⭐️" optional = false python-versions = ">=3.8" files = [ - {file = "questionary-2.0.1-py3-none-any.whl", hash = "sha256:8ab9a01d0b91b68444dff7f6652c1e754105533f083cbe27597c8110ecc230a2"}, - {file = "questionary-2.0.1.tar.gz", hash = "sha256:bcce898bf3dbb446ff62830c86c5c6fb9a22a54146f0f5597d3da43b10d8fc8b"}, + {file = "questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec"}, + {file = "questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587"}, ] [package.dependencies] -prompt_toolkit = ">=2.0,<=3.0.36" +prompt_toolkit = ">=2.0,<4.0" [[package]] name = "redis" @@ -3785,13 +3785,13 @@ doc = ["sphinx"] [[package]] name = "strelix-core" -version = "0.0.2" +version = "0.0.4" description = "github.com/Strelix/core" optional = false python-versions = "<4.0,>=3.10" files = [ - {file = "strelix_core-0.0.2-py3-none-any.whl", hash = "sha256:6db40dbce91df8ed0288a807fad06fdc1cd738b81303c036f5b469e3e929bc11"}, - {file = "strelix_core-0.0.2.tar.gz", hash = "sha256:0f1e5965643f87e8e03713b7c8de12d673574b0b802f95dbbc7c614a36d296ec"}, + {file = "strelix_core-0.0.4-py3-none-any.whl", hash = "sha256:35cde3c2f602d7ead25b516d955596c74f56765c7ce8534e7281a7f713fc22e3"}, + {file = "strelix_core-0.0.4.tar.gz", hash = "sha256:bb3a07ab0307d26bdaafb1813f85efe0c596444f8b41841b196fb9a8682dd138"}, ] [package.dependencies] @@ -4068,13 +4068,13 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20241221" +version = "6.0.12.20241230" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" files = [ - {file = "types_PyYAML-6.0.12.20241221-py3-none-any.whl", hash = "sha256:0657a4ff8411a030a2116a196e8e008ea679696b5b1a8e1a6aa8ebb737b34688"}, - {file = "types_pyyaml-6.0.12.20241221.tar.gz", hash = "sha256:4f149aa893ff6a46889a30af4c794b23833014c469cc57cbc3ad77498a58996f"}, + {file = "types_PyYAML-6.0.12.20241230-py3-none-any.whl", hash = "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6"}, + {file = "types_pyyaml-6.0.12.20241230.tar.gz", hash = "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c"}, ] [[package]] @@ -4337,4 +4337,4 @@ test = ["coverage (>=5.3)", "tomli (>=2.0.1)", "tox"] [metadata] lock-version = "2.0" python-versions = ">=3.10,<4.0" -content-hash = "c50d378c288aef708607d576653b329d64d314489d84b7bd732a0a5d52a9e2d8" +content-hash = "dc5f07be020a9e0e5ff847c130041a91ff8c709799483b57d6633d7d42a18974" diff --git a/pyproject.toml b/pyproject.toml index 05f075d8d..a13002b72 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,7 +22,7 @@ drf-yasg = "^1.21.7" setuptools = "^70.1.1" xhtml2pdf = "^0.2.16" stripe = "^10.8.0" -strelix-core = "^0.0.2" +strelix-core = { version = "0.0.4" } [tool.poetry.group.mypy.dependencies] mypy = "1.7.1" django-stubs = { version = "4.2.7" } diff --git a/settings/settings.py b/settings/settings.py index 4da453106..374133675 100644 --- a/settings/settings.py +++ b/settings/settings.py @@ -10,12 +10,15 @@ import stripe from core.config import CoreConfig +from django.conf import settings from django.contrib.messages import constants as messages from django.contrib.staticfiles.storage import FileSystemStorage # type: ignore from storages.backends.s3 import S3Storage from .helpers import get_var +core_config = CoreConfig() + # from backend.utils import appconfig DEBUG = True if get_var("DEBUG") in ["True", "true", "TRUE", True] else False @@ -131,6 +134,8 @@ "social_core.backends.google.GoogleOAuth2", ] +AUTH_USER_MODEL = "core.User" + SECRET_KEY = get_var("SECRET_KEY", default="secret_key") LOGIN_URL = "/auth/login/" @@ -288,8 +293,6 @@ } } -AUTH_USER_MODEL = "core.User" - LANGUAGE_CODE = "en-us" TIME_ZONE = "UTC" @@ -344,6 +347,7 @@ }, "handlers": { "console": { + "level": "DEBUG", "class": "logging.StreamHandler", "formatter": "simple", }, @@ -352,20 +356,26 @@ "django": { "handlers": ["console"], "level": "INFO", - "propagate": True, + "propagate": False, # Ensure propagation is False to avoid duplicates }, "django.db.backends": { "handlers": ["console"], - "level": get_var("DJANGO_LOG_LEVEL", default="INFO"), - "propagate": False, + "level": "INFO", # Set your desired logging level + "propagate": False, # Prevent propagation for database logs + }, + "django.request": { + "handlers": ["console"], + "level": "ERROR", + "propagate": False, # Only log errors for requests }, }, "root": { "handlers": ["console"], - "level": get_var("DJANGO_LOG_LEVEL", default="INFO"), + "level": "INFO", # Root logger level }, } +# Apply the configuration to the Django logging system logging.config.dictConfig(LOGGING) @@ -461,7 +471,11 @@ class CustomPrivateMediaStorage(FileSystemStorage): # type: ignore # This overr # CORE SETTINGS -EXPIRY_MODELS = CoreConfig().CORE_EXPIRY_MODELS + ["backend.InvoiceURL"] +core_config.EXPIRY_MODELS += ["backend.InvoiceURL"] +core_config.SETTINGS_PAGE_CONTEXT_HANDLERS.update( + {"email_templates": "backend.core.service.settings.view.email_templates.email_templates_context"} +) +# EXPIRY_MODELS = CoreConfig().EXPIRY_MODELS # SENDGRID_SANDBOX_MODE_IN_DEBUG = True if "test" in sys.argv[1:]: diff --git a/tailwind.config.js b/tailwind.config.js index f08b21cdf..00b0cb723 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,27 +1,45 @@ /** @type {import('tailwindcss').Config} */ -const {execSync} = require('child_process'); +const {spawnSync} = require('child_process'); +const path = require('path'); +const projectRoot = path.resolve(__dirname, '.'); -function getDjangoTemplates(appName) { - try { - // Get the path of the app's templates directory in the pip-installed package - const result = execSync( - `python -c "import importlib.util, os; app_spec = importlib.util.find_spec('${appName}'); print(os.path.join(os.path.dirname(app_spec.origin), 'templates'))"` - ).toString().trim(); +const getCoreTemplateFiles = () => { + const command = 'python'; // Requires virtualenv to be activated. + const args = ['manage.py', 'list_core_templates']; // Requires cwd to be set. + const options = {cwd: projectRoot}; + const result = spawnSync(command, args, options); - return result || ''; - } catch (error) { - console.error(`Failed to get templates for ${appName}:`, error); - return ''; + if (result.error) { + throw result.error; } -} -const coreTemplatesPath = getDjangoTemplates('core'); -const billingTemplatesPath = getDjangoTemplates('billing'); + if (result.status !== 0) { + console.log(result.stdout.toString(), result.stderr.toString()); + throw new Error(`Django management command exited with code ${result.status}`); + } + + const covert_to_template = (value) => { + new_value = value.trim() + + if (!new_value.startsWith("TMPL:")) { + return "" + } + + // new_value = value.replace(/\\/g, "/").replace("\\", "/").replace("\r", "") + "/**/*.html" + return new_value.slice(5) + "/**/*.html" + } -console.log('Resolved core templates path:', coreTemplatesPath); -console.log('Resolved billing templates path:', billingTemplatesPath); + const templateFiles = result.stdout.toString() + .split('\n') + .map((path) => covert_to_template(path)) + .filter(function (e) { + return e + }); // Remove empty strings, including last empty line. + console.log(templateFiles) + return templateFiles; +} module.exports = { mode: 'jit', @@ -29,12 +47,10 @@ module.exports = { './frontend/templates/**/*.html', './billing/templates/**/*.html', './components/**/*.html', - './frontend/templates/base/base.html', './backend/**/views/*.py', './backend/views/core/**/*.py', './assets/scripts/tableify.js', - coreTemplatesPath ? `${coreTemplatesPath}/**/*.html` : '', - billingTemplatesPath ? `${billingTemplatesPath}/**/*.html` : '', + ...getCoreTemplateFiles() ], safelist: [ 'alert',