Compare commits
1 Commits
feat/permi
...
8c6b25ded8
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c6b25ded8 |
@@ -1,25 +0,0 @@
|
|||||||
name: Deploy Amap
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
workflow_dispatch:
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Test backend
|
|
||||||
uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: "3.12"
|
|
||||||
- run: |
|
|
||||||
python -m pip install --upgrade pip
|
|
||||||
pip install -r backend/requirements.txt
|
|
||||||
pytest -sv
|
|
||||||
- name: Build & deploy
|
|
||||||
run: |
|
|
||||||
docker compose -f docker-compose.yaml up -d --build
|
|
||||||
docker compose -f docker-compose.yaml exec backend alembic upgrade head
|
|
||||||
27
README.md
27
README.md
@@ -2,14 +2,24 @@
|
|||||||
|
|
||||||
## backend\src\contracts\contracts.py
|
## backend\src\contracts\contracts.py
|
||||||
|
|
||||||
|
- Send contract to referer
|
||||||
- Extract recap
|
- Extract recap
|
||||||
|
- Extract all contracts
|
||||||
## Link products to a form
|
- store total price
|
||||||
|
|
||||||
## Wording
|
## Wording
|
||||||
|
|
||||||
- all translations
|
- all translations
|
||||||
|
|
||||||
|
## Draft / Publish form
|
||||||
|
|
||||||
|
- By default form is in draft mode
|
||||||
|
- Validate a form (button)
|
||||||
|
- check if productor
|
||||||
|
- check if shipments
|
||||||
|
- check products
|
||||||
|
- Publish
|
||||||
|
|
||||||
## Footer
|
## Footer
|
||||||
|
|
||||||
### Legal
|
### Legal
|
||||||
@@ -18,9 +28,14 @@
|
|||||||
|
|
||||||
### Contact
|
### Contact
|
||||||
|
|
||||||
## Pagination
|
## Migrations
|
||||||
|
|
||||||
## Confirmation modal on suppression
|
- use alembic for migration management
|
||||||
### Show on cascade deletion
|
|
||||||
|
|
||||||
## Update contract after (without registration)
|
## Filter forms in home view
|
||||||
|
|
||||||
|
## Only show current season (if multiple form, only show the one with latest start date)
|
||||||
|
|
||||||
|
## Update contract after register
|
||||||
|
|
||||||
|
## Default filter
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": "1",
|
"version": "1",
|
||||||
"name": "bruno",
|
"name": "amapcontract",
|
||||||
"type": "collection",
|
"type": "collection",
|
||||||
"ignore": [
|
"ignore": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
default_language_version:
|
|
||||||
python: python3.13
|
|
||||||
|
|
||||||
repos:
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
||||||
rev: v6.0.0
|
|
||||||
hooks:
|
|
||||||
- id: check-added-large-files
|
|
||||||
- id: trailing-whitespace
|
|
||||||
- id: check-ast
|
|
||||||
- id: check-builtin-literals
|
|
||||||
- id: check-docstring-first
|
|
||||||
- id: check-yaml
|
|
||||||
- id: check-toml
|
|
||||||
- id: mixed-line-ending
|
|
||||||
- id: end-of-file-fixer
|
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: check-pylint
|
|
||||||
name: check-pylint
|
|
||||||
entry: pylint -d R0801,R0903,W0511,W0603,C0103,R0902
|
|
||||||
language: system
|
|
||||||
types: [python]
|
|
||||||
pass_filenames: false
|
|
||||||
args:
|
|
||||||
- backend
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
FROM python:3.12
|
|
||||||
|
|
||||||
WORKDIR /code
|
|
||||||
|
|
||||||
RUN apt update && apt install -y weasyprint
|
|
||||||
|
|
||||||
COPY ./backend/requirements.txt /code/requirements.txt
|
|
||||||
|
|
||||||
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
|
||||||
|
|
||||||
COPY ./backend /code
|
|
||||||
|
|
||||||
CMD ["fastapi", "run", "src/main.py", "--port", "8000", "--forwarded-allow-ips", "*", "--proxy-headers"]
|
|
||||||
@@ -18,29 +18,6 @@ hatch shell
|
|||||||
fastapi dev src/main.py
|
fastapi dev src/main.py
|
||||||
```
|
```
|
||||||
|
|
||||||
### Migration
|
|
||||||
This repository use `alembic` for migrations
|
|
||||||
|
|
||||||
#### Create migration
|
|
||||||
```console
|
|
||||||
alembic revision --autogenerate -m "message"
|
|
||||||
```
|
|
||||||
#### Apply migration
|
|
||||||
```console
|
|
||||||
alembic upgrade head
|
|
||||||
```
|
|
||||||
## Tests
|
|
||||||
```
|
|
||||||
hatch run pytest
|
|
||||||
hatch run pytest --cov=src -vv
|
|
||||||
```
|
|
||||||
|
|
||||||
## Autoformat
|
|
||||||
```console
|
|
||||||
find -type f -name '*.py' ! -path 'alembic/*' -exec autopep8 --in-place --aggressive --aggressive '{}' \;
|
|
||||||
pylint -d R0801,R0903,W0511,W0603,C0103,R0902 .
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
`backend` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
`backend` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
||||||
|
|||||||
@@ -1,147 +0,0 @@
|
|||||||
# A generic, single database configuration.
|
|
||||||
|
|
||||||
[alembic]
|
|
||||||
# path to migration scripts.
|
|
||||||
# this is typically a path given in POSIX (e.g. forward slashes)
|
|
||||||
# format, relative to the token %(here)s which refers to the location of this
|
|
||||||
# ini file
|
|
||||||
script_location = %(here)s/alembic
|
|
||||||
|
|
||||||
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
|
||||||
# Uncomment the line below if you want the files to be prepended with date and time
|
|
||||||
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
|
|
||||||
# for all available tokens
|
|
||||||
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
|
|
||||||
# Or organize into date-based subdirectories (requires recursive_version_locations = true)
|
|
||||||
# file_template = %%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s
|
|
||||||
|
|
||||||
# sys.path path, will be prepended to sys.path if present.
|
|
||||||
# defaults to the current working directory. for multiple paths, the path separator
|
|
||||||
# is defined by "path_separator" below.
|
|
||||||
prepend_sys_path = .
|
|
||||||
|
|
||||||
|
|
||||||
# timezone to use when rendering the date within the migration file
|
|
||||||
# as well as the filename.
|
|
||||||
# If specified, requires the tzdata library which can be installed by adding
|
|
||||||
# `alembic[tz]` to the pip requirements.
|
|
||||||
# string value is passed to ZoneInfo()
|
|
||||||
# leave blank for localtime
|
|
||||||
# timezone =
|
|
||||||
|
|
||||||
# max length of characters to apply to the "slug" field
|
|
||||||
# truncate_slug_length = 40
|
|
||||||
|
|
||||||
# set to 'true' to run the environment during
|
|
||||||
# the 'revision' command, regardless of autogenerate
|
|
||||||
# revision_environment = false
|
|
||||||
|
|
||||||
# set to 'true' to allow .pyc and .pyo files without
|
|
||||||
# a source .py file to be detected as revisions in the
|
|
||||||
# versions/ directory
|
|
||||||
# sourceless = false
|
|
||||||
|
|
||||||
# version location specification; This defaults
|
|
||||||
# to <script_location>/versions. When using multiple version
|
|
||||||
# directories, initial revisions must be specified with --version-path.
|
|
||||||
# The path separator used here should be the separator specified by "path_separator"
|
|
||||||
# below.
|
|
||||||
# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions
|
|
||||||
|
|
||||||
# path_separator; This indicates what character is used to split lists of file
|
|
||||||
# paths, including version_locations and prepend_sys_path within configparser
|
|
||||||
# files such as alembic.ini.
|
|
||||||
# The default rendered in new alembic.ini files is "os", which uses os.pathsep
|
|
||||||
# to provide os-dependent path splitting.
|
|
||||||
#
|
|
||||||
# Note that in order to support legacy alembic.ini files, this default does NOT
|
|
||||||
# take place if path_separator is not present in alembic.ini. If this
|
|
||||||
# option is omitted entirely, fallback logic is as follows:
|
|
||||||
#
|
|
||||||
# 1. Parsing of the version_locations option falls back to using the legacy
|
|
||||||
# "version_path_separator" key, which if absent then falls back to the legacy
|
|
||||||
# behavior of splitting on spaces and/or commas.
|
|
||||||
# 2. Parsing of the prepend_sys_path option falls back to the legacy
|
|
||||||
# behavior of splitting on spaces, commas, or colons.
|
|
||||||
#
|
|
||||||
# Valid values for path_separator are:
|
|
||||||
#
|
|
||||||
# path_separator = :
|
|
||||||
# path_separator = ;
|
|
||||||
# path_separator = space
|
|
||||||
# path_separator = newline
|
|
||||||
#
|
|
||||||
# Use os.pathsep. Default configuration used for new projects.
|
|
||||||
path_separator = os
|
|
||||||
|
|
||||||
# set to 'true' to search source files recursively
|
|
||||||
# in each "version_locations" directory
|
|
||||||
# new in Alembic version 1.10
|
|
||||||
# recursive_version_locations = false
|
|
||||||
|
|
||||||
# the output encoding used when revision files
|
|
||||||
# are written from script.py.mako
|
|
||||||
# output_encoding = utf-8
|
|
||||||
|
|
||||||
# database URL. This is consumed by the user-maintained env.py script only.
|
|
||||||
# other means of configuring database URLs may be customized within the env.py
|
|
||||||
# file.
|
|
||||||
|
|
||||||
[post_write_hooks]
|
|
||||||
# post_write_hooks defines scripts or Python functions that are run
|
|
||||||
# on newly generated revision scripts. See the documentation for further
|
|
||||||
# detail and examples
|
|
||||||
|
|
||||||
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
|
||||||
# hooks = black
|
|
||||||
# black.type = console_scripts
|
|
||||||
# black.entrypoint = black
|
|
||||||
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
|
||||||
|
|
||||||
# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module
|
|
||||||
# hooks = ruff
|
|
||||||
# ruff.type = module
|
|
||||||
# ruff.module = ruff
|
|
||||||
# ruff.options = check --fix REVISION_SCRIPT_FILENAME
|
|
||||||
|
|
||||||
# Alternatively, use the exec runner to execute a binary found on your PATH
|
|
||||||
# hooks = ruff
|
|
||||||
# ruff.type = exec
|
|
||||||
# ruff.executable = ruff
|
|
||||||
# ruff.options = check --fix REVISION_SCRIPT_FILENAME
|
|
||||||
|
|
||||||
# Logging configuration. This is also consumed by the user-maintained
|
|
||||||
# env.py script only.
|
|
||||||
[loggers]
|
|
||||||
keys = root,sqlalchemy,alembic
|
|
||||||
|
|
||||||
[handlers]
|
|
||||||
keys = console
|
|
||||||
|
|
||||||
[formatters]
|
|
||||||
keys = generic
|
|
||||||
|
|
||||||
[logger_root]
|
|
||||||
level = WARNING
|
|
||||||
handlers = console
|
|
||||||
qualname =
|
|
||||||
|
|
||||||
[logger_sqlalchemy]
|
|
||||||
level = WARNING
|
|
||||||
handlers =
|
|
||||||
qualname = sqlalchemy.engine
|
|
||||||
|
|
||||||
[logger_alembic]
|
|
||||||
level = INFO
|
|
||||||
handlers =
|
|
||||||
qualname = alembic
|
|
||||||
|
|
||||||
[handler_console]
|
|
||||||
class = StreamHandler
|
|
||||||
args = (sys.stderr,)
|
|
||||||
level = NOTSET
|
|
||||||
formatter = generic
|
|
||||||
|
|
||||||
[formatter_generic]
|
|
||||||
format = %(levelname)-5.5s [%(name)s] %(message)s
|
|
||||||
datefmt = %H:%M:%S
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Generic single-database configuration.
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
from logging.config import fileConfig
|
|
||||||
|
|
||||||
from sqlalchemy import engine_from_config
|
|
||||||
from sqlalchemy import pool
|
|
||||||
|
|
||||||
from alembic import context
|
|
||||||
|
|
||||||
from sqlmodel import SQLModel
|
|
||||||
from src.settings import settings
|
|
||||||
from src.models import *
|
|
||||||
# this is the Alembic Config object, which provides
|
|
||||||
# access to the values within the .ini file in use.
|
|
||||||
config = context.config
|
|
||||||
|
|
||||||
# Interpret the config file for Python logging.
|
|
||||||
# This line sets up loggers basically.
|
|
||||||
if config.config_file_name is not None:
|
|
||||||
fileConfig(config.config_file_name)
|
|
||||||
|
|
||||||
# add your model's MetaData object here
|
|
||||||
# for 'autogenerate' support
|
|
||||||
# from myapp import mymodel
|
|
||||||
# target_metadata = mymodel.Base.metadata
|
|
||||||
target_metadata = SQLModel.metadata
|
|
||||||
|
|
||||||
# other values from the config, defined by the needs of env.py,
|
|
||||||
# can be acquired:
|
|
||||||
config.set_main_option(
|
|
||||||
"sqlalchemy.url", f'postgresql://{settings.db_user}:{settings.db_pass}@{settings.db_host}:5432/{settings.db_name}')
|
|
||||||
# ... etc.
|
|
||||||
|
|
||||||
|
|
||||||
def run_migrations_offline() -> None:
|
|
||||||
"""Run migrations in 'offline' mode.
|
|
||||||
|
|
||||||
This configures the context with just a URL
|
|
||||||
and not an Engine, though an Engine is acceptable
|
|
||||||
here as well. By skipping the Engine creation
|
|
||||||
we don't even need a DBAPI to be available.
|
|
||||||
|
|
||||||
Calls to context.execute() here emit the given string to the
|
|
||||||
script output.
|
|
||||||
|
|
||||||
"""
|
|
||||||
url = config.get_main_option("sqlalchemy.url")
|
|
||||||
context.configure(
|
|
||||||
url=url,
|
|
||||||
target_metadata=target_metadata,
|
|
||||||
literal_binds=True,
|
|
||||||
dialect_opts={"paramstyle": "named"},
|
|
||||||
)
|
|
||||||
|
|
||||||
with context.begin_transaction():
|
|
||||||
context.run_migrations()
|
|
||||||
|
|
||||||
|
|
||||||
def run_migrations_online() -> None:
|
|
||||||
"""Run migrations in 'online' mode.
|
|
||||||
|
|
||||||
In this scenario we need to create an Engine
|
|
||||||
and associate a connection with the context.
|
|
||||||
|
|
||||||
"""
|
|
||||||
connectable = engine_from_config(
|
|
||||||
config.get_section(config.config_ini_section, {}),
|
|
||||||
prefix="sqlalchemy.",
|
|
||||||
poolclass=pool.NullPool,
|
|
||||||
)
|
|
||||||
|
|
||||||
with connectable.connect() as connection:
|
|
||||||
context.configure(
|
|
||||||
connection=connection, target_metadata=target_metadata
|
|
||||||
)
|
|
||||||
|
|
||||||
with context.begin_transaction():
|
|
||||||
context.run_migrations()
|
|
||||||
|
|
||||||
|
|
||||||
if context.is_offline_mode():
|
|
||||||
run_migrations_offline()
|
|
||||||
else:
|
|
||||||
run_migrations_online()
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
"""${message}
|
|
||||||
|
|
||||||
Revision ID: ${up_revision}
|
|
||||||
Revises: ${down_revision | comma,n}
|
|
||||||
Create Date: ${create_date}
|
|
||||||
|
|
||||||
"""
|
|
||||||
from typing import Sequence, Union
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
${imports if imports else ""}
|
|
||||||
import sqlmodel.sql.sqltypes
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision: str = ${repr(up_revision)}
|
|
||||||
down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)}
|
|
||||||
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
|
||||||
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
|
||||||
"""Upgrade schema."""
|
|
||||||
${upgrades if upgrades else "pass"}
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
|
||||||
"""Downgrade schema."""
|
|
||||||
${downgrades if downgrades else "pass"}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
"""message
|
|
||||||
|
|
||||||
Revision ID: 7854064278ce
|
|
||||||
Revises: c0b1073a8394
|
|
||||||
Create Date: 2026-02-20 17:17:25.739406
|
|
||||||
|
|
||||||
"""
|
|
||||||
from typing import Sequence, Union
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
import sqlmodel.sql.sqltypes
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision: str = '7854064278ce'
|
|
||||||
down_revision: Union[str, Sequence[str], None] = 'c0b1073a8394'
|
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
|
||||||
"""Upgrade schema."""
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.add_column(
|
|
||||||
'paymentmethod',
|
|
||||||
sa.Column(
|
|
||||||
'max',
|
|
||||||
sa.Integer(),
|
|
||||||
nullable=True))
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
|
||||||
"""Downgrade schema."""
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.drop_column('paymentmethod', 'max')
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
"""Initial repository
|
|
||||||
|
|
||||||
Revision ID: c0b1073a8394
|
|
||||||
Revises:
|
|
||||||
Create Date: 2026-02-20 00:09:35.920486
|
|
||||||
|
|
||||||
"""
|
|
||||||
from typing import Sequence, Union
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
import sqlmodel.sql.sqltypes
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision: str = 'c0b1073a8394'
|
|
||||||
down_revision: Union[str, Sequence[str], None] = None
|
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
|
||||||
"""Upgrade schema."""
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.create_table(
|
|
||||||
'contracttype',
|
|
||||||
sa.Column(
|
|
||||||
'id',
|
|
||||||
sa.Integer(),
|
|
||||||
nullable=False),
|
|
||||||
sa.Column(
|
|
||||||
'name',
|
|
||||||
sqlmodel.sql.sqltypes.AutoString(),
|
|
||||||
nullable=False),
|
|
||||||
sa.PrimaryKeyConstraint('id'))
|
|
||||||
op.create_table(
|
|
||||||
'productor', sa.Column(
|
|
||||||
'name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column(
|
|
||||||
'address', sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column(
|
|
||||||
'type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column(
|
|
||||||
'id', sa.Integer(), nullable=False), sa.PrimaryKeyConstraint('id'))
|
|
||||||
op.create_table('template',
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_table(
|
|
||||||
'user', sa.Column(
|
|
||||||
'name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column(
|
|
||||||
'email', sqlmodel.sql.sqltypes.AutoString(), nullable=False), sa.Column(
|
|
||||||
'id', sa.Integer(), nullable=False), sa.PrimaryKeyConstraint('id'))
|
|
||||||
op.create_table('form',
|
|
||||||
sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('productor_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('referer_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('season', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('start', sa.Date(), nullable=False),
|
|
||||||
sa.Column('end', sa.Date(), nullable=False),
|
|
||||||
sa.Column('minimum_shipment_value', sa.Float(), nullable=True),
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['productor_id'], ['productor.id'], ),
|
|
||||||
sa.ForeignKeyConstraint(['referer_id'], ['user.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_table('paymentmethod',
|
|
||||||
sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('details', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('productor_id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['productor_id'], ['productor.id'], ondelete='CASCADE'),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_table('product',
|
|
||||||
sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('unit', sa.Enum('GRAMS', 'KILO', 'PIECE', name='unit'), nullable=False),
|
|
||||||
sa.Column('price', sa.Float(), nullable=True),
|
|
||||||
sa.Column('price_kg', sa.Float(), nullable=True),
|
|
||||||
sa.Column('quantity', sa.Float(), nullable=True),
|
|
||||||
sa.Column('quantity_unit', sqlmodel.sql.sqltypes.AutoString(), nullable=True),
|
|
||||||
sa.Column('type', sa.Enum('OCCASIONAL', 'RECCURENT', name='producttype'), nullable=False),
|
|
||||||
sa.Column('productor_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['productor_id'], ['productor.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_table(
|
|
||||||
'usercontracttypelink', sa.Column(
|
|
||||||
'user_id', sa.Integer(), nullable=False), sa.Column(
|
|
||||||
'contract_type_id', sa.Integer(), nullable=False), sa.ForeignKeyConstraint(
|
|
||||||
['contract_type_id'], ['contracttype.id'], ), sa.ForeignKeyConstraint(
|
|
||||||
['user_id'], ['user.id'], ), sa.PrimaryKeyConstraint(
|
|
||||||
'user_id', 'contract_type_id'))
|
|
||||||
op.create_table('contract',
|
|
||||||
sa.Column('firstname', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('lastname', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('email', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('phone', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('payment_method', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('cheque_quantity', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('form_id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('file', sa.LargeBinary(), nullable=True),
|
|
||||||
sa.Column('total_price', sa.Float(), nullable=True),
|
|
||||||
sa.ForeignKeyConstraint(['form_id'], ['form.id'], ondelete='CASCADE'),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_table('shipment',
|
|
||||||
sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('date', sa.Date(), nullable=False),
|
|
||||||
sa.Column('form_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['form_id'], ['form.id'], ondelete='CASCADE'),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_table('cheque',
|
|
||||||
sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('value', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('contract_id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['contract_id'], ['contract.id'], ondelete='CASCADE'),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_table('contractproduct',
|
|
||||||
sa.Column('product_id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('shipment_id', sa.Integer(), nullable=True),
|
|
||||||
sa.Column('quantity', sa.Float(), nullable=False),
|
|
||||||
sa.Column('id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('contract_id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['contract_id'], ['contract.id'], ondelete='CASCADE'),
|
|
||||||
sa.ForeignKeyConstraint(['product_id'], ['product.id'], ondelete='CASCADE'),
|
|
||||||
sa.ForeignKeyConstraint(['shipment_id'], ['shipment.id'], ondelete='CASCADE'),
|
|
||||||
sa.PrimaryKeyConstraint('id')
|
|
||||||
)
|
|
||||||
op.create_table('shipmentproductlink',
|
|
||||||
sa.Column('shipment_id', sa.Integer(), nullable=False),
|
|
||||||
sa.Column('product_id', sa.Integer(), nullable=False),
|
|
||||||
sa.ForeignKeyConstraint(['product_id'], ['product.id'], ),
|
|
||||||
sa.ForeignKeyConstraint(['shipment_id'], ['shipment.id'], ),
|
|
||||||
sa.PrimaryKeyConstraint('shipment_id', 'product_id')
|
|
||||||
)
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
|
||||||
"""Downgrade schema."""
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.drop_table('shipmentproductlink')
|
|
||||||
op.drop_table('contractproduct')
|
|
||||||
op.drop_table('cheque')
|
|
||||||
op.drop_table('shipment')
|
|
||||||
op.drop_table('contract')
|
|
||||||
op.drop_table('usercontracttypelink')
|
|
||||||
op.drop_table('product')
|
|
||||||
op.drop_table('paymentmethod')
|
|
||||||
op.drop_table('form')
|
|
||||||
op.drop_table('user')
|
|
||||||
op.drop_table('template')
|
|
||||||
op.drop_table('productor')
|
|
||||||
op.drop_table('contracttype')
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
"""message
|
|
||||||
|
|
||||||
Revision ID: e777ed5729ce
|
|
||||||
Revises: 7854064278ce
|
|
||||||
Create Date: 2026-02-23 13:53:09.999893
|
|
||||||
|
|
||||||
"""
|
|
||||||
from typing import Sequence, Union
|
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
|
||||||
|
|
||||||
import sqlmodel.sql.sqltypes
|
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
|
||||||
revision: str = 'e777ed5729ce'
|
|
||||||
down_revision: Union[str, Sequence[str], None] = '7854064278ce'
|
|
||||||
branch_labels: Union[str, Sequence[str], None] = None
|
|
||||||
depends_on: Union[str, Sequence[str], None] = None
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
|
||||||
"""Upgrade schema."""
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.add_column(
|
|
||||||
'form',
|
|
||||||
sa.Column(
|
|
||||||
'visible',
|
|
||||||
sa.Boolean(),
|
|
||||||
nullable=False,
|
|
||||||
default=False,
|
|
||||||
server_default="False"))
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
|
||||||
"""Downgrade schema."""
|
|
||||||
# ### commands auto generated by Alembic - please adjust! ###
|
|
||||||
op.drop_column('form', 'visible')
|
|
||||||
# ### end Alembic commands ###
|
|
||||||
@@ -29,14 +29,7 @@ dependencies = [
|
|||||||
"cryptography",
|
"cryptography",
|
||||||
"requests",
|
"requests",
|
||||||
"weasyprint",
|
"weasyprint",
|
||||||
"odfdo",
|
"odfdo"
|
||||||
"alembic",
|
|
||||||
"pytest",
|
|
||||||
"pytest-cov",
|
|
||||||
"pytest-mock",
|
|
||||||
"autopep8",
|
|
||||||
"prek",
|
|
||||||
"pylint",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
alembic==1.18.4
|
|
||||||
annotated-doc==0.0.4
|
|
||||||
annotated-types==0.7.0
|
|
||||||
anyio==4.12.1
|
|
||||||
astroid==4.0.4
|
|
||||||
autopep8==2.3.2
|
|
||||||
brotli==1.2.0
|
|
||||||
certifi==2026.2.25
|
|
||||||
cffi==2.0.0
|
|
||||||
charset-normalizer==3.4.4
|
|
||||||
click==8.3.1
|
|
||||||
coverage==7.13.4
|
|
||||||
cryptography==46.0.5
|
|
||||||
cssselect2==0.9.0
|
|
||||||
dill==0.4.1
|
|
||||||
dnspython==2.8.0
|
|
||||||
email-validator==2.3.0
|
|
||||||
fastapi==0.135.1
|
|
||||||
fastapi-cli==0.0.24
|
|
||||||
fastapi-cloud-cli==0.14.0
|
|
||||||
fastar==0.8.0
|
|
||||||
fonttools==4.61.1
|
|
||||||
greenlet==3.3.2
|
|
||||||
h11==0.16.0
|
|
||||||
httpcore==1.0.9
|
|
||||||
httptools==0.7.1
|
|
||||||
httpx==0.28.1
|
|
||||||
idna==3.11
|
|
||||||
iniconfig==2.3.0
|
|
||||||
isort==8.0.1
|
|
||||||
Jinja2==3.1.6
|
|
||||||
lxml==6.0.2
|
|
||||||
Mako==1.3.10
|
|
||||||
markdown-it-py==4.0.0
|
|
||||||
MarkupSafe==3.0.3
|
|
||||||
mccabe==0.7.0
|
|
||||||
mdurl==0.1.2
|
|
||||||
odfdo==3.21.0
|
|
||||||
packaging==26.0
|
|
||||||
pillow==12.1.1
|
|
||||||
platformdirs==4.9.2
|
|
||||||
pluggy==1.6.0
|
|
||||||
prek==0.3.4
|
|
||||||
psycopg2-binary==2.9.11
|
|
||||||
pycodestyle==2.14.0
|
|
||||||
pycparser==3.0
|
|
||||||
pydantic==2.12.5
|
|
||||||
pydantic-extra-types==2.11.0
|
|
||||||
pydantic-settings==2.13.1
|
|
||||||
pydantic_core==2.41.5
|
|
||||||
pydyf==0.12.1
|
|
||||||
Pygments==2.19.2
|
|
||||||
PyJWT==2.11.0
|
|
||||||
pylint==4.0.5
|
|
||||||
pyphen==0.17.2
|
|
||||||
pytest==9.0.2
|
|
||||||
pytest-cov==7.0.0
|
|
||||||
pytest-mock==3.15.1
|
|
||||||
python-dotenv==1.2.2
|
|
||||||
python-multipart==0.0.22
|
|
||||||
PyYAML==6.0.3
|
|
||||||
requests==2.32.5
|
|
||||||
rich==14.3.3
|
|
||||||
rich-toolkit==0.19.7
|
|
||||||
rignore==0.7.6
|
|
||||||
sentry-sdk==2.53.0
|
|
||||||
shellingham==1.5.4
|
|
||||||
SQLAlchemy==2.0.47
|
|
||||||
sqlmodel==0.0.37
|
|
||||||
starlette==0.52.1
|
|
||||||
tinycss2==1.5.1
|
|
||||||
tinyhtml5==2.0.0
|
|
||||||
tomlkit==0.14.0
|
|
||||||
typer==0.24.1
|
|
||||||
typing-inspection==0.4.2
|
|
||||||
typing_extensions==4.15.0
|
|
||||||
urllib3==2.6.3
|
|
||||||
uvicorn==0.41.0
|
|
||||||
uvloop==0.22.1
|
|
||||||
watchfiles==1.1.1
|
|
||||||
weasyprint==68.1
|
|
||||||
webencodings==0.5.1
|
|
||||||
websockets==16.0
|
|
||||||
zopfli==0.4.1
|
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
import secrets
|
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
from urllib.parse import urlencode
|
from fastapi import APIRouter, Security, HTTPException, Depends, Request, Cookie
|
||||||
|
|
||||||
import jwt
|
|
||||||
import requests
|
|
||||||
import src.messages as messages
|
|
||||||
import src.users.service as service
|
|
||||||
from fastapi import (APIRouter, Cookie, Depends, HTTPException, Request,
|
|
||||||
Security)
|
|
||||||
from fastapi.responses import RedirectResponse, Response
|
from fastapi.responses import RedirectResponse, Response
|
||||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||||
from jwt import PyJWKClient
|
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
|
import jwt
|
||||||
|
from jwt import PyJWKClient
|
||||||
|
|
||||||
|
from src.settings import AUTH_URL, TOKEN_URL, JWKS_URL, ISSUER, LOGOUT_URL, settings
|
||||||
|
import src.users.service as service
|
||||||
from src.database import get_session
|
from src.database import get_session
|
||||||
from src.models import User, UserCreate, UserPublic
|
from src.models import UserCreate, User, UserPublic
|
||||||
from src.settings import (AUTH_URL, ISSUER, JWKS_URL, LOGOUT_URL, TOKEN_URL,
|
|
||||||
settings)
|
import secrets
|
||||||
|
import requests
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
import src.messages as messages
|
||||||
|
|
||||||
router = APIRouter(prefix='/auth')
|
router = APIRouter(prefix='/auth')
|
||||||
|
|
||||||
jwk_client = PyJWKClient(JWKS_URL)
|
jwk_client = PyJWKClient(JWKS_URL)
|
||||||
security = HTTPBearer()
|
security = HTTPBearer()
|
||||||
|
|
||||||
|
|
||||||
@router.get('/logout')
|
@router.get('/logout')
|
||||||
def logout():
|
def logout(
|
||||||
|
refresh_token: Annotated[str | None, Cookie()] = None,
|
||||||
|
):
|
||||||
params = {
|
params = {
|
||||||
'client_id': settings.keycloak_client_id,
|
'client_id': settings.keycloak_client_id,
|
||||||
'post_logout_redirect_uri': settings.origins,
|
'post_logout_redirect_uri': settings.origins,
|
||||||
@@ -34,20 +34,26 @@ def logout():
|
|||||||
key='access_token',
|
key='access_token',
|
||||||
path='/',
|
path='/',
|
||||||
secure=not settings.debug,
|
secure=not settings.debug,
|
||||||
samesite='strict',
|
samesite='lax',
|
||||||
)
|
)
|
||||||
response.delete_cookie(
|
response.delete_cookie(
|
||||||
key='refresh_token',
|
key='refresh_token',
|
||||||
path='/',
|
path='/',
|
||||||
secure=not settings.debug,
|
secure=not settings.debug,
|
||||||
samesite='strict',
|
samesite='lax',
|
||||||
)
|
)
|
||||||
response.delete_cookie(
|
response.delete_cookie(
|
||||||
key='id_token',
|
key='id_token',
|
||||||
path='/',
|
path='/',
|
||||||
secure=not settings.debug,
|
secure=not settings.debug,
|
||||||
samesite='strict',
|
samesite='lax',
|
||||||
)
|
)
|
||||||
|
# if refresh_token:
|
||||||
|
# requests.post(LOGOUT_URL, data={
|
||||||
|
# 'client_id': settings.keycloak_client_id,
|
||||||
|
# 'client_secret': settings.keycloak_client_secret,
|
||||||
|
# 'refresh_token': refresh_token
|
||||||
|
# })
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@@ -61,11 +67,9 @@ def login():
|
|||||||
'redirect_uri': settings.keycloak_redirect_uri,
|
'redirect_uri': settings.keycloak_redirect_uri,
|
||||||
'state': state,
|
'state': state,
|
||||||
}
|
}
|
||||||
request_url = requests.Request(
|
request_url = requests.Request('GET', AUTH_URL, params=params).prepare().url
|
||||||
'GET', AUTH_URL, params=params).prepare().url
|
|
||||||
return RedirectResponse(request_url)
|
return RedirectResponse(request_url)
|
||||||
|
|
||||||
|
|
||||||
@router.get('/callback')
|
@router.get('/callback')
|
||||||
def callback(code: str, session: Session = Depends(get_session)):
|
def callback(code: str, session: Session = Depends(get_session)):
|
||||||
data = {
|
data = {
|
||||||
@@ -81,17 +85,15 @@ def callback(code: str, session: Session = Depends(get_session)):
|
|||||||
response = requests.post(TOKEN_URL, data=data, headers=headers)
|
response = requests.post(TOKEN_URL, data=data, headers=headers)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=404,
|
status_code=400,
|
||||||
detail=messages.Messages.not_found('token')
|
detail=messages.failtogettoken
|
||||||
)
|
)
|
||||||
|
|
||||||
token_data = response.json()
|
token_data = response.json()
|
||||||
|
|
||||||
id_token = token_data['id_token']
|
id_token = token_data['id_token']
|
||||||
decoded_token = jwt.decode(id_token, options={'verify_signature': False})
|
decoded_token = jwt.decode(id_token, options={'verify_signature': False})
|
||||||
decoded_access_token = jwt.decode(
|
decoded_access_token = jwt.decode(token_data['access_token'], options={'verify_signature': False})
|
||||||
token_data['access_token'], options={
|
|
||||||
'verify_signature': False})
|
|
||||||
resource_access = decoded_access_token.get('resource_access')
|
resource_access = decoded_access_token.get('resource_access')
|
||||||
if not resource_access:
|
if not resource_access:
|
||||||
data = {
|
data = {
|
||||||
@@ -99,7 +101,7 @@ def callback(code: str, session: Session = Depends(get_session)):
|
|||||||
'client_secret': settings.keycloak_client_secret,
|
'client_secret': settings.keycloak_client_secret,
|
||||||
'refresh_token': token_data['refresh_token'],
|
'refresh_token': token_data['refresh_token'],
|
||||||
}
|
}
|
||||||
requests.post(LOGOUT_URL, data=data)
|
res = requests.post(LOGOUT_URL, data=data)
|
||||||
resp = RedirectResponse(f'{settings.origins}?userNotAllowed=true')
|
resp = RedirectResponse(f'{settings.origins}?userNotAllowed=true')
|
||||||
return resp
|
return resp
|
||||||
roles = resource_access.get(settings.keycloak_client_id)
|
roles = resource_access.get(settings.keycloak_client_id)
|
||||||
@@ -109,13 +111,13 @@ def callback(code: str, session: Session = Depends(get_session)):
|
|||||||
'client_secret': settings.keycloak_client_secret,
|
'client_secret': settings.keycloak_client_secret,
|
||||||
'refresh_token': token_data['refresh_token'],
|
'refresh_token': token_data['refresh_token'],
|
||||||
}
|
}
|
||||||
requests.post(LOGOUT_URL, data=data)
|
res = requests.post(LOGOUT_URL, data=data)
|
||||||
resp = RedirectResponse(f'{settings.origins}?userNotAllowed=true')
|
resp = RedirectResponse(f'{settings.origins}?userNotAllowed=true')
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
user_create = UserCreate(
|
user_create = UserCreate(
|
||||||
email=decoded_token.get('email'),
|
email=decoded_token.get('email'),
|
||||||
name=decoded_token.get('name'),
|
name=decoded_token.get('preferred_username'),
|
||||||
role_names=roles['roles']
|
role_names=roles['roles']
|
||||||
)
|
)
|
||||||
service.get_or_create_user(session, user_create)
|
service.get_or_create_user(session, user_create)
|
||||||
@@ -125,7 +127,7 @@ def callback(code: str, session: Session = Depends(get_session)):
|
|||||||
value=token_data['access_token'],
|
value=token_data['access_token'],
|
||||||
httponly=True,
|
httponly=True,
|
||||||
secure=not settings.debug,
|
secure=not settings.debug,
|
||||||
samesite='strict',
|
samesite='lax',
|
||||||
max_age=settings.max_age
|
max_age=settings.max_age
|
||||||
)
|
)
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
@@ -133,7 +135,7 @@ def callback(code: str, session: Session = Depends(get_session)):
|
|||||||
value=token_data['refresh_token'] or '',
|
value=token_data['refresh_token'] or '',
|
||||||
httponly=True,
|
httponly=True,
|
||||||
secure=not settings.debug,
|
secure=not settings.debug,
|
||||||
samesite='strict',
|
samesite='lax',
|
||||||
max_age=30 * 24 * settings.max_age
|
max_age=30 * 24 * settings.max_age
|
||||||
)
|
)
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
@@ -141,69 +143,47 @@ def callback(code: str, session: Session = Depends(get_session)):
|
|||||||
value=token_data['id_token'],
|
value=token_data['id_token'],
|
||||||
httponly=True,
|
httponly=True,
|
||||||
secure=not settings.debug,
|
secure=not settings.debug,
|
||||||
samesite='strict',
|
samesite='lax',
|
||||||
max_age=settings.max_age
|
max_age=settings.max_age
|
||||||
)
|
)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
def verify_token(token: str):
|
def verify_token(token: str):
|
||||||
try:
|
try:
|
||||||
signing_key = jwk_client.get_signing_key_from_jwt(token)
|
signing_key = jwk_client.get_signing_key_from_jwt(token)
|
||||||
decoded = jwt.decode(
|
decoded = jwt.decode(token, options={'verify_signature': False})
|
||||||
|
payload = jwt.decode(
|
||||||
token,
|
token,
|
||||||
signing_key.key,
|
signing_key.key,
|
||||||
algorithms=['RS256'],
|
algorithms=['RS256'],
|
||||||
audience=settings.keycloak_client_id,
|
audience=settings.keycloak_client_id,
|
||||||
issuer=ISSUER,
|
issuer=ISSUER,
|
||||||
leeway=60,
|
|
||||||
)
|
)
|
||||||
return decoded
|
return payload
|
||||||
except jwt.ExpiredSignatureError:
|
except jwt.ExpiredSignatureError:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=401, detail=messages.tokenexipired)
|
||||||
status_code=401,
|
|
||||||
detail=messages.Messages.tokenexipired
|
|
||||||
)
|
|
||||||
except jwt.InvalidTokenError:
|
except jwt.InvalidTokenError:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=401, detail=messages.invalidtoken)
|
||||||
status_code=401,
|
|
||||||
detail=messages.Messages.invalidtoken
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_user(
|
def get_current_user(request: Request, session: Session = Depends(get_session)):
|
||||||
request: Request,
|
|
||||||
session: Session = Depends(get_session)):
|
|
||||||
access_token = request.cookies.get('access_token')
|
access_token = request.cookies.get('access_token')
|
||||||
if not access_token:
|
if not access_token:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=401, detail=messages.notauthenticated)
|
||||||
status_code=401,
|
|
||||||
detail=messages.Messages.notauthenticated
|
|
||||||
)
|
|
||||||
payload = verify_token(access_token)
|
payload = verify_token(access_token)
|
||||||
if not payload:
|
if not payload:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=401, detail='aze')
|
||||||
status_code=401,
|
|
||||||
detail='aze'
|
|
||||||
)
|
|
||||||
email = payload.get('email')
|
email = payload.get('email')
|
||||||
|
|
||||||
if not email:
|
if not email:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=401, detail=messages.notauthenticated)
|
||||||
status_code=401,
|
|
||||||
detail=messages.Messages.notauthenticated
|
|
||||||
)
|
|
||||||
|
|
||||||
user = session.exec(select(User).where(User.email == email)).first()
|
user = session.exec(select(User).where(User.email == email)).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=401, detail=messages.usernotfound)
|
||||||
status_code=401,
|
|
||||||
detail=messages.Messages.not_found('user')
|
|
||||||
)
|
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
@router.post('/refresh')
|
@router.post('/refresh')
|
||||||
def refresh_token(refresh_token: Annotated[str | None, Cookie()] = None):
|
def refresh_token(refresh_token: Annotated[str | None, Cookie()] = None):
|
||||||
refresh = refresh_token
|
refresh = refresh_token
|
||||||
@@ -219,8 +199,8 @@ def refresh_token(refresh_token: Annotated[str | None, Cookie()] = None):
|
|||||||
result = requests.post(TOKEN_URL, data=data, headers=headers)
|
result = requests.post(TOKEN_URL, data=data, headers=headers)
|
||||||
if result.status_code != 200:
|
if result.status_code != 200:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=404,
|
status_code=400,
|
||||||
detail=messages.Messages.not_found('token')
|
detail=messages.failtogettoken
|
||||||
)
|
)
|
||||||
|
|
||||||
token_data = result.json()
|
token_data = result.json()
|
||||||
@@ -230,7 +210,7 @@ def refresh_token(refresh_token: Annotated[str | None, Cookie()] = None):
|
|||||||
value=token_data['access_token'],
|
value=token_data['access_token'],
|
||||||
httponly=True,
|
httponly=True,
|
||||||
secure=True if settings.debug == False else True,
|
secure=True if settings.debug == False else True,
|
||||||
samesite='strict',
|
samesite='lax',
|
||||||
max_age=settings.max_age
|
max_age=settings.max_age
|
||||||
)
|
)
|
||||||
response.set_cookie(
|
response.set_cookie(
|
||||||
@@ -238,20 +218,11 @@ def refresh_token(refresh_token: Annotated[str | None, Cookie()] = None):
|
|||||||
value=token_data['refresh_token'] or '',
|
value=token_data['refresh_token'] or '',
|
||||||
httponly=True,
|
httponly=True,
|
||||||
secure=True if settings.debug == False else True,
|
secure=True if settings.debug == False else True,
|
||||||
samesite='strict',
|
samesite='lax',
|
||||||
max_age=30 * 24 * settings.max_age
|
max_age=4
|
||||||
)
|
|
||||||
response.set_cookie(
|
|
||||||
key='id_token',
|
|
||||||
value=token_data['id_token'],
|
|
||||||
httponly=True,
|
|
||||||
secure=not settings.debug,
|
|
||||||
samesite='strict',
|
|
||||||
max_age=settings.max_age
|
|
||||||
)
|
)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
@router.get('/user/me')
|
@router.get('/user/me')
|
||||||
def me(user: UserPublic = Depends(get_current_user)):
|
def me(user: UserPublic = Depends(get_current_user)):
|
||||||
if not user:
|
if not user:
|
||||||
@@ -262,6 +233,6 @@ def me(user: UserPublic = Depends(get_current_user)):
|
|||||||
'name': user.name,
|
'name': user.name,
|
||||||
'email': user.email,
|
'email': user.email,
|
||||||
'id': user.id,
|
'id': user.id,
|
||||||
'roles': user.roles
|
'roles': [role.name for role in user.roles]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,27 +1,18 @@
|
|||||||
"""Router for contract resource"""
|
|
||||||
import io
|
|
||||||
import zipfile
|
|
||||||
|
|
||||||
import src.contracts.service as service
|
|
||||||
import src.forms.service as form_service
|
|
||||||
import src.messages as messages
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.contracts.generate_contract import (generate_html_contract,
|
|
||||||
generate_recap)
|
|
||||||
from src.database import get_session
|
from src.database import get_session
|
||||||
|
from sqlmodel import Session
|
||||||
|
from src.contracts.generate_contract import generate_html_contract, generate_recap
|
||||||
|
from src.auth.auth import get_current_user
|
||||||
|
import src.models as models
|
||||||
|
import src.messages as messages
|
||||||
|
import src.contracts.service as service
|
||||||
|
import src.forms.service as form_service
|
||||||
|
import io
|
||||||
|
import zipfile
|
||||||
router = APIRouter(prefix='/contracts')
|
router = APIRouter(prefix='/contracts')
|
||||||
|
|
||||||
|
def compute_recurrent_prices(products_quantities: list[dict], nb_shipment: int):
|
||||||
def compute_recurrent_prices(
|
|
||||||
products_quantities: list[dict],
|
|
||||||
nb_shipment: int
|
|
||||||
):
|
|
||||||
"""Compute price for recurrent products"""
|
|
||||||
result = 0
|
result = 0
|
||||||
for product_quantity in products_quantities:
|
for product_quantity in products_quantities:
|
||||||
product = product_quantity['product']
|
product = product_quantity['product']
|
||||||
@@ -29,45 +20,25 @@ def compute_recurrent_prices(
|
|||||||
result += compute_product_price(product, quantity, nb_shipment)
|
result += compute_product_price(product, quantity, nb_shipment)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def compute_occasional_prices(occasionals: list[dict]):
|
def compute_occasional_prices(occasionals: list[dict]):
|
||||||
"""Compute prices for occassional products"""
|
|
||||||
result = 0
|
result = 0
|
||||||
for occasional in occasionals:
|
for occasional in occasionals:
|
||||||
result += occasional['price']
|
result += occasional['price']
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def compute_product_price(product: models.Product, quantity: int, nb_shipment: int = 1):
|
||||||
def compute_product_price(
|
product_quantity_unit = 1 if product.unit == models.Unit.KILO else 1000
|
||||||
product: models.Product,
|
final_quantity = quantity if product.price else quantity / product_quantity_unit
|
||||||
quantity: int,
|
final_price = product.price if product.price else product.price_kg
|
||||||
nb_shipment: int = 1
|
|
||||||
):
|
|
||||||
"""Compute price for a product"""
|
|
||||||
product_quantity_unit = (
|
|
||||||
1 if product.unit == models.Unit.KILO else 1000
|
|
||||||
)
|
|
||||||
final_quantity = (
|
|
||||||
quantity if product.price else quantity / product_quantity_unit
|
|
||||||
)
|
|
||||||
final_price = (
|
|
||||||
product.price if product.price else product.price_kg
|
|
||||||
)
|
|
||||||
return final_price * final_quantity * nb_shipment
|
return final_price * final_quantity * nb_shipment
|
||||||
|
|
||||||
|
|
||||||
def find_dict_in_list(lst, key, value):
|
def find_dict_in_list(lst, key, value):
|
||||||
"""Find the index of a dictionnary in a list of dictionnaries given a key
|
|
||||||
and a value.
|
|
||||||
"""
|
|
||||||
for i, dic in enumerate(lst):
|
for i, dic in enumerate(lst):
|
||||||
if dic[key].id == value:
|
if dic[key].id == value:
|
||||||
return i
|
return i
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
|
|
||||||
def create_occasional_dict(contract_products: list[models.ContractProduct]):
|
def create_occasional_dict(contract_products: list[models.ContractProduct]):
|
||||||
"""Create a dictionnary of occasional products"""
|
|
||||||
result = []
|
result = []
|
||||||
for contract_product in contract_products:
|
for contract_product in contract_products:
|
||||||
existing_id = find_dict_in_list(
|
existing_id = find_dict_in_list(
|
||||||
@@ -98,175 +69,64 @@ def create_occasional_dict(contract_products: list[models.ContractProduct]):
|
|||||||
)
|
)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@router.post('/')
|
||||||
@router.post('')
|
|
||||||
async def create_contract(
|
async def create_contract(
|
||||||
contract: models.ContractCreate,
|
contract: models.ContractCreate,
|
||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
):
|
):
|
||||||
"""Create contract route"""
|
|
||||||
new_contract = service.create_one(session, contract)
|
new_contract = service.create_one(session, contract)
|
||||||
occasional_contract_products = list(
|
occasional_contract_products = list(filter(lambda contract_product: contract_product.product.type == models.ProductType.OCCASIONAL, new_contract.products))
|
||||||
filter(
|
|
||||||
lambda contract_product: (
|
|
||||||
contract_product.product.type == models.ProductType.OCCASIONAL
|
|
||||||
),
|
|
||||||
new_contract.products
|
|
||||||
)
|
|
||||||
)
|
|
||||||
occasionals = create_occasional_dict(occasional_contract_products)
|
occasionals = create_occasional_dict(occasional_contract_products)
|
||||||
recurrents = list(
|
recurrents = list(map(lambda x: {"product": x.product, "quantity": x.quantity}, filter(lambda contract_product: contract_product.product.type == models.ProductType.RECCURENT, new_contract.products)))
|
||||||
map(
|
recurrent_price = compute_recurrent_prices(recurrents, len(new_contract.form.shipments))
|
||||||
lambda x: {'product': x.product, 'quantity': x.quantity},
|
|
||||||
filter(
|
|
||||||
lambda contract_product: (
|
|
||||||
contract_product.product.type ==
|
|
||||||
models.ProductType.RECCURENT
|
|
||||||
),
|
|
||||||
new_contract.products
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
recurrent_price = compute_recurrent_prices(
|
|
||||||
recurrents,
|
|
||||||
len(new_contract.form.shipments)
|
|
||||||
)
|
|
||||||
price = recurrent_price + compute_occasional_prices(occasionals)
|
price = recurrent_price + compute_occasional_prices(occasionals)
|
||||||
cheques = list(
|
total_price = '{:10.2f}'.format(price)
|
||||||
map(
|
cheques = list(map(lambda x: {"name": x.name, "value": x.value}, new_contract.cheques))
|
||||||
lambda x: {'name': x.name, 'value': x.value},
|
# TODO: send contract to referer
|
||||||
new_contract.cheques
|
|
||||||
)
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
pdf_bytes = generate_html_contract(
|
pdf_bytes = generate_html_contract(
|
||||||
new_contract,
|
new_contract,
|
||||||
cheques,
|
cheques,
|
||||||
occasionals,
|
occasionals,
|
||||||
recurrents,
|
recurrents,
|
||||||
'{:10.2f}'.format(recurrent_price),
|
recurrent_price,
|
||||||
'{:10.2f}'.format(price)
|
total_price
|
||||||
)
|
)
|
||||||
pdf_file = io.BytesIO(pdf_bytes)
|
pdf_file = io.BytesIO(pdf_bytes)
|
||||||
contract_id = (
|
contract_id = f'{new_contract.firstname}_{new_contract.lastname}_{new_contract.form.productor.type}_{new_contract.form.season}'
|
||||||
f'{new_contract.firstname}_'
|
|
||||||
f'{new_contract.lastname}_'
|
|
||||||
f'{new_contract.form.productor.type}_'
|
|
||||||
f'{new_contract.form.season}'
|
|
||||||
)
|
|
||||||
service.add_contract_file(session, new_contract.id, pdf_bytes, price)
|
service.add_contract_file(session, new_contract.id, pdf_bytes, price)
|
||||||
except Exception as error:
|
except Exception as e:
|
||||||
raise HTTPException(
|
print(e)
|
||||||
status_code=400,
|
raise HTTPException(status_code=400, detail=messages.pdferror)
|
||||||
detail=messages.pdferror
|
|
||||||
) from error
|
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
pdf_file,
|
pdf_file,
|
||||||
media_type='application/pdf',
|
media_type='application/pdf',
|
||||||
headers={
|
headers={
|
||||||
'Content-Disposition': (
|
'Content-Disposition': f'attachment; filename=contract_{contract_id}.pdf'
|
||||||
f'attachment; filename=contract_{contract_id}.pdf'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@router.get('/', response_model=list[models.ContractPublic])
|
||||||
@router.get('/{form_id}/base')
|
|
||||||
async def get_base_contract_template(
|
|
||||||
form_id: int,
|
|
||||||
session: Session = Depends(get_session),
|
|
||||||
):
|
|
||||||
"""Get contract template route"""
|
|
||||||
form = form_service.get_one(session, form_id)
|
|
||||||
recurrents = [
|
|
||||||
{'product': product, 'quantity': None}
|
|
||||||
for product in form.productor.products
|
|
||||||
if product.type == models.ProductType.RECCURENT
|
|
||||||
]
|
|
||||||
occasionals = [{
|
|
||||||
'shipment': sh,
|
|
||||||
'price': None,
|
|
||||||
'products': [{'product': pr, 'quantity': None} for pr in sh.products]
|
|
||||||
} for sh in form.shipments]
|
|
||||||
empty_contract = models.ContractPublic(
|
|
||||||
firstname='',
|
|
||||||
form=form,
|
|
||||||
lastname='',
|
|
||||||
email='',
|
|
||||||
phone='',
|
|
||||||
products=[],
|
|
||||||
payment_method='cheque',
|
|
||||||
cheque_quantity=3,
|
|
||||||
total_price=0,
|
|
||||||
id=1
|
|
||||||
)
|
|
||||||
cheques = [
|
|
||||||
{'name': None, 'value': None},
|
|
||||||
{'name': None, 'value': None},
|
|
||||||
{'name': None, 'value': None}
|
|
||||||
]
|
|
||||||
try:
|
|
||||||
pdf_bytes = generate_html_contract(
|
|
||||||
empty_contract,
|
|
||||||
cheques,
|
|
||||||
occasionals,
|
|
||||||
recurrents,
|
|
||||||
)
|
|
||||||
pdf_file = io.BytesIO(pdf_bytes)
|
|
||||||
contract_id = (
|
|
||||||
f'{empty_contract.form.productor.type}_'
|
|
||||||
f'{empty_contract.form.season}'
|
|
||||||
)
|
|
||||||
except Exception as error:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400,
|
|
||||||
detail=messages.pdferror
|
|
||||||
) from error
|
|
||||||
return StreamingResponse(
|
|
||||||
pdf_file,
|
|
||||||
media_type='application/pdf',
|
|
||||||
headers={
|
|
||||||
'Content-Disposition': (
|
|
||||||
f'attachment; filename=contract_{contract_id}.pdf'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('', response_model=list[models.ContractPublic])
|
|
||||||
def get_contracts(
|
def get_contracts(
|
||||||
forms: list[str] = Query([]),
|
forms: list[str] = Query([]),
|
||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
user: models.User = Depends(get_current_user)
|
user: models.User = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""Get all contracts route"""
|
|
||||||
return service.get_all(session, user, forms)
|
return service.get_all(session, user, forms)
|
||||||
|
|
||||||
|
@router.get('/{id}/file')
|
||||||
@router.get('/{_id}/file')
|
|
||||||
def get_contract_file(
|
def get_contract_file(
|
||||||
_id: int,
|
id: int,
|
||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
user: models.User = Depends(get_current_user)
|
user: models.User = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""Get a contract file (in pdf) route"""
|
if not service.is_allowed(session, user, id):
|
||||||
if not service.is_allowed(session, user, _id):
|
raise HTTPException(status_code=403, detail=messages.notallowed)
|
||||||
raise HTTPException(
|
contract = service.get_one(session, id)
|
||||||
status_code=403,
|
|
||||||
detail=messages.Messages.not_allowed('contract', 'get')
|
|
||||||
)
|
|
||||||
contract = service.get_one(session, _id)
|
|
||||||
if contract is None:
|
if contract is None:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
status_code=404,
|
filename = f'{contract.form.name.replace(' ', '_')}_{contract.form.season}_{contract.firstname}-{contract.lastname}'
|
||||||
detail=messages.Messages.not_found('contract')
|
|
||||||
)
|
|
||||||
filename = (
|
|
||||||
f'{contract.form.name.replace(' ', '_')}_'
|
|
||||||
f'{contract.form.season}_'
|
|
||||||
f'{contract.firstname}_'
|
|
||||||
f'{contract.lastname}'
|
|
||||||
)
|
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
io.BytesIO(contract.file),
|
io.BytesIO(contract.file),
|
||||||
media_type='application/pdf',
|
media_type='application/pdf',
|
||||||
@@ -275,37 +135,23 @@ def get_contract_file(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get('/{form_id}/files')
|
@router.get('/{form_id}/files')
|
||||||
def get_contract_files(
|
def get_contract_files(
|
||||||
form_id: int,
|
form_id: int,
|
||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
user: models.User = Depends(get_current_user)
|
user: models.User = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""Get all contract files for a given form"""
|
|
||||||
if not form_service.is_allowed(session, user, form_id):
|
if not form_service.is_allowed(session, user, form_id):
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=403, detail=messages.notallowed)
|
||||||
status_code=403,
|
|
||||||
detail=messages.Messages.not_allowed('contracts', 'get')
|
|
||||||
)
|
|
||||||
form = form_service.get_one(session, form_id=form_id)
|
form = form_service.get_one(session, form_id=form_id)
|
||||||
contracts = service.get_all(session, user, forms=[form.name])
|
contracts = service.get_all(session, user, forms=[form.name])
|
||||||
zipped_contracts = io.BytesIO()
|
zipped_contracts = io.BytesIO()
|
||||||
with zipfile.ZipFile(
|
with zipfile.ZipFile(zipped_contracts, "a", zipfile.ZIP_DEFLATED, False) as zip_file:
|
||||||
zipped_contracts,
|
|
||||||
'a',
|
|
||||||
zipfile.ZIP_DEFLATED,
|
|
||||||
False
|
|
||||||
) as zip_file:
|
|
||||||
for contract in contracts:
|
for contract in contracts:
|
||||||
contract_filename = (
|
contract_filename = f'{contract.form.name.replace(' ', '_')}_{contract.form.season}_{contract.firstname}-{contract.lastname}.pdf'
|
||||||
f'{contract.form.name.replace(' ', '_')}_'
|
|
||||||
f'{contract.form.season}_'
|
|
||||||
f'{contract.firstname}_'
|
|
||||||
f'{contract.lastname}'
|
|
||||||
)
|
|
||||||
zip_file.writestr(contract_filename, contract.file)
|
zip_file.writestr(contract_filename, contract.file)
|
||||||
filename = f'{form.name.replace(' ', '_')}_{form.season}'
|
|
||||||
|
filename = f'{form.name.replace(" ", "_")}_{form.season}'
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
io.BytesIO(zipped_contracts.getvalue()),
|
io.BytesIO(zipped_contracts.getvalue()),
|
||||||
media_type='application/zip',
|
media_type='application/zip',
|
||||||
@@ -314,69 +160,39 @@ def get_contract_files(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get('/{form_id}/recap')
|
@router.get('/{form_id}/recap')
|
||||||
def get_contract_recap(
|
def get_contract_recap(
|
||||||
form_id: int,
|
form_id: int,
|
||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
user: models.User = Depends(get_current_user)
|
user: models.User = Depends(get_current_user)
|
||||||
):
|
):
|
||||||
"""Get a contract recap for a given form"""
|
|
||||||
if not form_service.is_allowed(session, user, form_id):
|
if not form_service.is_allowed(session, user, form_id):
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=403, detail=messages.notallowed)
|
||||||
status_code=403,
|
|
||||||
detail=messages.Messages.not_allowed('contract recap', 'get')
|
|
||||||
)
|
|
||||||
form = form_service.get_one(session, form_id=form_id)
|
form = form_service.get_one(session, form_id=form_id)
|
||||||
contracts = service.get_all(session, user, forms=[form.name])
|
contracts = service.get_all(session, user, forms=[form.name])
|
||||||
|
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
io.BytesIO(generate_recap(contracts, form)),
|
io.BytesIO(generate_recap(contracts, form)),
|
||||||
media_type='application/zip',
|
media_type='application/zip',
|
||||||
headers={
|
headers={
|
||||||
'Content-Disposition': (
|
'Content-Disposition': f'attachment; filename=filename.ods'
|
||||||
'attachment; filename=filename.ods'
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@router.get('/{id}', response_model=models.ContractPublic)
|
||||||
@router.get('/{_id}', response_model=models.ContractPublic)
|
def get_contract(id: int, session: Session = Depends(get_session), user: models.User = Depends(get_current_user)):
|
||||||
def get_contract(
|
if not service.is_allowed(session, user, id):
|
||||||
_id: int,
|
raise HTTPException(status_code=403, detail=messages.notallowed)
|
||||||
session: Session = Depends(get_session),
|
result = service.get_one(session, id)
|
||||||
user: models.User = Depends(get_current_user)
|
|
||||||
):
|
|
||||||
"""Get a contract route"""
|
|
||||||
if not service.is_allowed(session, user, _id):
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=403,
|
|
||||||
detail=messages.Messages.not_allowed('contract', 'get')
|
|
||||||
)
|
|
||||||
result = service.get_one(session, _id)
|
|
||||||
if result is None:
|
if result is None:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
status_code=404,
|
|
||||||
detail=messages.Messages.not_found('contract')
|
|
||||||
)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@router.delete('/{id}', response_model=models.ContractPublic)
|
||||||
@router.delete('/{_id}', response_model=models.ContractPublic)
|
def delete_contract(id: int, session: Session = Depends(get_session), user: models.User = Depends(get_current_user)):
|
||||||
def delete_contract(
|
if not service.is_allowed(session, user, id):
|
||||||
_id: int,
|
raise HTTPException(status_code=403, detail=messages.notallowed)
|
||||||
session: Session = Depends(get_session),
|
result = service.delete_one(session, id)
|
||||||
user: models.User = Depends(get_current_user)
|
|
||||||
):
|
|
||||||
"""Delete contract route"""
|
|
||||||
if not service.is_allowed(session, user, _id):
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=403,
|
|
||||||
detail=messages.Messages.not_allowed('contract', 'delete')
|
|
||||||
)
|
|
||||||
result = service.delete_one(session, _id)
|
|
||||||
if result is None:
|
if result is None:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
status_code=404,
|
|
||||||
detail=messages.Messages.not_found('contract')
|
|
||||||
)
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1,26 +1,21 @@
|
|||||||
|
|
||||||
import html
|
|
||||||
import io
|
|
||||||
import pathlib
|
|
||||||
|
|
||||||
import jinja2
|
import jinja2
|
||||||
from odfdo import Cell, Document, Row, Table
|
import src.models as models
|
||||||
from src import models
|
import html
|
||||||
from weasyprint import HTML
|
from weasyprint import HTML
|
||||||
|
import io
|
||||||
|
|
||||||
def generate_html_contract(
|
def generate_html_contract(
|
||||||
contract: models.Contract,
|
contract: models.Contract,
|
||||||
cheques: list[dict],
|
cheques: list[dict],
|
||||||
occasionals: list[dict],
|
occasionals: list[dict],
|
||||||
reccurents: list[dict],
|
reccurents: list[dict],
|
||||||
recurrent_price: float | None = None,
|
recurrent_price: float,
|
||||||
total_price: float | None = None
|
total_price: float
|
||||||
):
|
):
|
||||||
template_dir = pathlib.Path("./src/contracts/templates").resolve()
|
template_dir = "./src/contracts/templates"
|
||||||
template_loader = jinja2.FileSystemLoader(searchpath=template_dir)
|
template_loader = jinja2.FileSystemLoader(searchpath=template_dir)
|
||||||
template_env = jinja2.Environment(
|
template_env = jinja2.Environment(loader=template_loader, autoescape=jinja2.select_autoescape(["html", "xml"]))
|
||||||
loader=template_loader, autoescape=jinja2.select_autoescape(["html", "xml"]))
|
|
||||||
template_file = "layout.html"
|
template_file = "layout.html"
|
||||||
template = template_env.get_template(template_file)
|
template = template_env.get_template(template_file)
|
||||||
output_text = template.render(
|
output_text = template.render(
|
||||||
@@ -31,51 +26,95 @@ def generate_html_contract(
|
|||||||
referer_email=contract.form.referer.email,
|
referer_email=contract.form.referer.email,
|
||||||
productor_name=contract.form.productor.name,
|
productor_name=contract.form.productor.name,
|
||||||
productor_address=contract.form.productor.address,
|
productor_address=contract.form.productor.address,
|
||||||
payment_methods_map={
|
payment_methods_map={"cheque": "Ordre du chèque", "transfer": "virements"},
|
||||||
"cheque": "Ordre du chèque",
|
|
||||||
"transfer": "virements"},
|
|
||||||
productor_payment_methods=contract.form.productor.payment_methods,
|
productor_payment_methods=contract.form.productor.payment_methods,
|
||||||
member_name=f'{
|
member_name=f'{html.escape(contract.firstname)} {html.escape(contract.lastname)}',
|
||||||
html.escape(
|
member_email=html.escape(contract.email),
|
||||||
contract.firstname)} {
|
member_phone=html.escape(contract.phone),
|
||||||
html.escape(
|
|
||||||
contract.lastname)}',
|
|
||||||
member_email=html.escape(
|
|
||||||
contract.email),
|
|
||||||
member_phone=html.escape(
|
|
||||||
contract.phone),
|
|
||||||
contract_start_date=contract.form.start,
|
contract_start_date=contract.form.start,
|
||||||
contract_end_date=contract.form.end,
|
contract_end_date=contract.form.end,
|
||||||
occasionals=occasionals,
|
occasionals=occasionals,
|
||||||
recurrents=reccurents,
|
recurrents=reccurents,
|
||||||
recurrent_price=recurrent_price,
|
recurrent_price=recurrent_price,
|
||||||
total_price=total_price,
|
total_price=total_price,
|
||||||
contract_payment_method={
|
contract_payment_method={"cheque": "chèque", "transfer": "virements"}[contract.payment_method],
|
||||||
"cheque": "chèque",
|
cheques=cheques
|
||||||
"transfer": "virements"}[
|
)
|
||||||
contract.payment_method],
|
options = {
|
||||||
cheques=cheques)
|
'page-size': 'Letter',
|
||||||
|
'margin-top': '0.5in',
|
||||||
|
'margin-right': '0.5in',
|
||||||
|
'margin-bottom': '0.5in',
|
||||||
|
'margin-left': '0.5in',
|
||||||
|
'encoding': "UTF-8",
|
||||||
|
'print-media-type': True,
|
||||||
|
"disable-javascript": True,
|
||||||
|
"disable-external-links": True,
|
||||||
|
'enable-local-file-access': False,
|
||||||
|
"disable-local-file-access": True,
|
||||||
|
"no-images": True,
|
||||||
|
}
|
||||||
|
|
||||||
return HTML(
|
return HTML(
|
||||||
string=output_text,
|
string=output_text,
|
||||||
base_url=template_dir,
|
base_url=template_dir
|
||||||
).write_pdf()
|
).write_pdf()
|
||||||
|
|
||||||
|
def flatten(xss):
|
||||||
|
return [x for xs in xss for x in xs]
|
||||||
|
|
||||||
|
from odfdo import Document, Table, Row, Cell
|
||||||
|
from odfdo.element import Element
|
||||||
def generate_recap(
|
def generate_recap(
|
||||||
contracts: list[models.Contract],
|
contracts: list[models.Contract],
|
||||||
form: models.Form,
|
form: models.Form,
|
||||||
):
|
):
|
||||||
|
print(form.productor.products)
|
||||||
|
recurrents = [pr.name for pr in form.productor.products if pr.type == models.ProductType.RECCURENT]
|
||||||
|
recurrents.sort()
|
||||||
|
occasionnals = [pr.name for pr in form.productor.products if pr.type == models.ProductType.OCCASIONAL]
|
||||||
|
occasionnals.sort()
|
||||||
|
shipments = form.shipments
|
||||||
|
occasionnals_header = [occ for shipment in shipments for occ in occasionnals]
|
||||||
|
shipment_header = flatten([[shipment.name] + ["" * len(occasionnals)] for shipment in shipments])
|
||||||
|
|
||||||
|
product_unit_map = {
|
||||||
|
"1": "g",
|
||||||
|
"2": "kg",
|
||||||
|
"3": "p"
|
||||||
|
}
|
||||||
|
|
||||||
data = [
|
data = [
|
||||||
["nom", "email"],
|
["", ""] + ["" * len(recurrents)] + shipment_header,
|
||||||
|
["nom", "email"] + recurrents + occasionnals_header + ["remarques", "name"],
|
||||||
|
*[
|
||||||
|
[
|
||||||
|
f'{contract.firstname} {contract.lastname}',
|
||||||
|
f'{contract.email}',
|
||||||
|
*[f'{pr.quantity} {product_unit_map[pr.product.unit]}' for pr in sorted(contract.products, key=lambda x: x.product.name) if pr.product.type == models.ProductType.RECCURENT],
|
||||||
|
*[f'{pr.quantity} {product_unit_map[pr.product.unit]}' for pr in sorted(contract.products, key=lambda x: x.product.name) if pr.product.type == models.ProductType.OCCASIONAL],
|
||||||
|
"",
|
||||||
|
f'{contract.firstname} {contract.lastname}',
|
||||||
|
] for contract in contracts
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
doc = Document("spreadsheet")
|
doc = Document("spreadsheet")
|
||||||
sheet = Table(name="Recap")
|
sheet = Table(name="Recap")
|
||||||
sheet.set_values(data)
|
sheet.set_values(data)
|
||||||
|
|
||||||
|
offset = 0
|
||||||
|
index = 2 + len(recurrents)
|
||||||
|
for i in range(len(shipments)):
|
||||||
|
index = index + offset
|
||||||
|
print(index, index+len(occasionnals) - 1)
|
||||||
|
sheet.set_span((index, 0, index+len(occasionnals) - 1, 0), merge=True)
|
||||||
|
offset += len(occasionnals)
|
||||||
|
|
||||||
doc.body.append(sheet)
|
doc.body.append(sheet)
|
||||||
|
|
||||||
buffer = io.BytesIO()
|
buffer = io.BytesIO()
|
||||||
doc.save(buffer)
|
doc.save(buffer)
|
||||||
|
|
||||||
return buffer.getvalue()
|
return buffer.getvalue()
|
||||||
|
|
||||||
|
|||||||
@@ -1,57 +1,28 @@
|
|||||||
"""Contract service responsible for read, create, update and delete contracts"""
|
|
||||||
from sqlalchemy.orm import selectinload
|
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src import models
|
import src.models as models
|
||||||
|
|
||||||
|
|
||||||
def get_all(
|
def get_all(
|
||||||
session: Session,
|
session: Session,
|
||||||
user: models.User,
|
user: models.User,
|
||||||
forms: list[str] | None = None,
|
forms: list[str] = [],
|
||||||
form_id: int | None = None,
|
form_id: int | None = None,
|
||||||
) -> list[models.ContractPublic]:
|
) -> list[models.ContractPublic]:
|
||||||
"""Get all contracts"""
|
statement = select(models.Contract)\
|
||||||
statement = (
|
.join(models.Form, models.Contract.form_id == models.Form.id)\
|
||||||
select(models.Contract)
|
.join(models.Productor, models.Form.productor_id == models.Productor.id)\
|
||||||
.join(
|
.where(models.Productor.type.in_([r.name for r in user.roles]))\
|
||||||
models.Form,
|
|
||||||
models.Contract.form_id == models.Form.id
|
|
||||||
)
|
|
||||||
.join(
|
|
||||||
models.Productor,
|
|
||||||
models.Form.productor_id == models.Productor.id
|
|
||||||
)
|
|
||||||
.where(
|
|
||||||
models.Productor.type.in_(
|
|
||||||
[r.name for r in user.roles]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
if len(forms) > 0:
|
||||||
if forms:
|
|
||||||
statement = statement.where(models.Form.name.in_(forms))
|
statement = statement.where(models.Form.name.in_(forms))
|
||||||
if form_id:
|
if form_id:
|
||||||
statement = statement.where(models.Form.id == form_id)
|
statement = statement.where(models.Form.id == form_id)
|
||||||
return session.exec(statement.order_by(models.Contract.id)).all()
|
return session.exec(statement.order_by(models.Contract.id)).all()
|
||||||
|
|
||||||
|
def get_one(session: Session, contract_id: int) -> models.ContractPublic:
|
||||||
def get_one(
|
|
||||||
session: Session,
|
|
||||||
contract_id: int
|
|
||||||
) -> models.ContractPublic:
|
|
||||||
"""Get one contract"""
|
|
||||||
return session.get(models.Contract, contract_id)
|
return session.get(models.Contract, contract_id)
|
||||||
|
|
||||||
|
def create_one(session: Session, contract: models.ContractCreate) -> models.ContractPublic:
|
||||||
def create_one(
|
contract_create = contract.model_dump(exclude_unset=True, exclude=["products", "cheques"])
|
||||||
session: Session,
|
|
||||||
contract: models.ContractCreate
|
|
||||||
) -> models.ContractPublic:
|
|
||||||
"""Create one contract"""
|
|
||||||
contract_create = contract.model_dump(
|
|
||||||
exclude_unset=True,
|
|
||||||
exclude=["products", "cheques"]
|
|
||||||
)
|
|
||||||
new_contract = models.Contract(**contract_create)
|
new_contract = models.Contract(**contract_create)
|
||||||
|
|
||||||
new_contract.cheques = [
|
new_contract.cheques = [
|
||||||
@@ -74,27 +45,10 @@ def create_one(
|
|||||||
session.add(new_contract)
|
session.add(new_contract)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(new_contract)
|
session.refresh(new_contract)
|
||||||
|
return new_contract
|
||||||
|
|
||||||
statement = (
|
def add_contract_file(session: Session, id: int, file: bytes, price: float):
|
||||||
select(models.Contract)
|
statement = select(models.Contract).where(models.Contract.id == id)
|
||||||
.where(models.Contract.id == new_contract.id)
|
|
||||||
.options(
|
|
||||||
selectinload(models.Contract.form)
|
|
||||||
.selectinload(models.Form.productor)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return session.exec(statement).one()
|
|
||||||
|
|
||||||
|
|
||||||
def add_contract_file(
|
|
||||||
session: Session,
|
|
||||||
_id: int,
|
|
||||||
file: bytes,
|
|
||||||
price: float
|
|
||||||
):
|
|
||||||
"""Add a file to an existing contract"""
|
|
||||||
statement = select(models.Contract).where(models.Contract.id == _id)
|
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
contract = result.first()
|
contract = result.first()
|
||||||
contract.total_price = price
|
contract.total_price = price
|
||||||
@@ -104,14 +58,8 @@ def add_contract_file(
|
|||||||
session.refresh(contract)
|
session.refresh(contract)
|
||||||
return contract
|
return contract
|
||||||
|
|
||||||
|
def update_one(session: Session, id: int, contract: models.ContractUpdate) -> models.ContractPublic:
|
||||||
def update_one(
|
statement = select(models.Contract).where(models.Contract.id == id)
|
||||||
session: Session,
|
|
||||||
_id: int,
|
|
||||||
contract: models.ContractUpdate
|
|
||||||
) -> models.ContractPublic:
|
|
||||||
"""Update one contract"""
|
|
||||||
statement = select(models.Contract).where(models.Contract.id == _id)
|
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
new_contract = result.first()
|
new_contract = result.first()
|
||||||
if not new_contract:
|
if not new_contract:
|
||||||
@@ -124,13 +72,8 @@ def update_one(
|
|||||||
session.refresh(new_contract)
|
session.refresh(new_contract)
|
||||||
return new_contract
|
return new_contract
|
||||||
|
|
||||||
|
def delete_one(session: Session, id: int) -> models.ContractPublic:
|
||||||
def delete_one(
|
statement = select(models.Contract).where(models.Contract.id == id)
|
||||||
session: Session,
|
|
||||||
_id: int
|
|
||||||
) -> models.ContractPublic:
|
|
||||||
"""Delete one contract"""
|
|
||||||
statement = select(models.Contract).where(models.Contract.id == _id)
|
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
contract = result.first()
|
contract = result.first()
|
||||||
if not contract:
|
if not contract:
|
||||||
@@ -140,29 +83,11 @@ def delete_one(
|
|||||||
session.commit()
|
session.commit()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def is_allowed(session: Session, user: models.User, id: int) -> bool:
|
||||||
def is_allowed(
|
statement = select(models.Contract)\
|
||||||
session: Session,
|
.join(models.Form, models.Contract.form_id == models.Form.id)\
|
||||||
user: models.User,
|
.join(models.Productor, models.Form.productor_id == models.Productor.id)\
|
||||||
_id: int
|
.where(models.Contract.id == id)\
|
||||||
) -> bool:
|
.where(models.Productor.type.in_([r.name for r in user.roles]))\
|
||||||
"""Determine if a user is allowed to access a contract by id"""
|
|
||||||
statement = (
|
|
||||||
select(models.Contract)
|
|
||||||
.join(
|
|
||||||
models.Form,
|
|
||||||
models.Contract.form_id == models.Form.id
|
|
||||||
)
|
|
||||||
.join(
|
|
||||||
models.Productor,
|
|
||||||
models.Form.productor_id == models.Productor.id
|
|
||||||
)
|
|
||||||
.where(models.Contract.id == _id)
|
|
||||||
.where(
|
|
||||||
models.Productor.type.in_(
|
|
||||||
[r.name for r in user.roles]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
|
||||||
return len(session.exec(statement).all()) > 0
|
return len(session.exec(statement).all()) > 0
|
||||||
@@ -151,6 +151,10 @@
|
|||||||
<th>Saison du contrat</th>
|
<th>Saison du contrat</th>
|
||||||
<td>{{contract_season}}</td>
|
<td>{{contract_season}}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Type de contrat</th>
|
||||||
|
<td>{{contract_type}}</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Référent·e</th>
|
<th>Référent·e</th>
|
||||||
<td>{{referer_name}}</td>
|
<td>{{referer_name}}</td>
|
||||||
@@ -274,14 +278,14 @@
|
|||||||
else ""}}
|
else ""}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{rec.quantity if rec.quantity != None else ""}}{{"g" if rec.product.unit == "1" else "kg" if
|
{{rec.quantity}}{{"g" if rec.product.unit == "1" else "kg" if
|
||||||
rec.product.unit == "2" else "p" }}
|
rec.product.unit == "2" else "p" }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" colspan="4">Total</th>
|
<th scope="row" colspan="4">Total</th>
|
||||||
<td>{{recurrent_price if recurrent_price else ""}}€</td>
|
<td>{{recurrent_price}}€</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -317,15 +321,14 @@
|
|||||||
product.product.quantity_unit != None else ""}}
|
product.product.quantity_unit != None else ""}}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{product.quantity if product.quantity != None
|
{{product.quantity}}{{"g" if product.product.unit == "1" else
|
||||||
else ""}}{{"g" if product.product.unit == "1" else
|
|
||||||
"kg" if product.product.unit == "2" else "p" }}
|
"kg" if product.product.unit == "2" else "p" }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor%}
|
{% endfor%}
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row" colspan="4">Total</th>
|
<th scope="row" colspan="4">Total</th>
|
||||||
<td>{{occasional.price if occasional.price else ""}}€</td>
|
<td>{{occasional.price}}€</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -334,7 +337,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="total-box">
|
<div class="total-box">
|
||||||
<div class="total-label">Prix Total :</div>
|
<div class="total-label">Prix Total :</div>
|
||||||
<div class="total-price">{{total_price if total_price else ""}}€</div>
|
<div class="total-price">{{total_price}}€</div>
|
||||||
</div>
|
</div>
|
||||||
<h4>Paiement par {{contract_payment_method}}</h4>
|
<h4>Paiement par {{contract_payment_method}}</h4>
|
||||||
{% if contract_payment_method == "chèque" %}
|
{% if contract_payment_method == "chèque" %}
|
||||||
@@ -343,14 +346,14 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{% for cheque in cheques %}
|
{% for cheque in cheques %}
|
||||||
<th>Cheque n°{{cheque.name if cheque.name else ""}}</th>
|
<th>Cheque n°{{cheque.name}}</th>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
{% for cheque in cheques %}
|
{% for cheque in cheques %}
|
||||||
<td>{{cheque.value if cheque.value else ""}}€</td>
|
<td>{{cheque.value}}€</td>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
from sqlmodel import Session, SQLModel, create_engine
|
from sqlmodel import create_engine, SQLModel, Session
|
||||||
from src.settings import settings
|
from src.settings import settings
|
||||||
|
|
||||||
engine = create_engine(
|
engine = create_engine(f'postgresql://{settings.db_user}:{settings.db_pass}@{settings.db_host}:54321/{settings.db_name}')
|
||||||
f'postgresql://{settings.db_user}:{settings.db_pass}@{settings.db_host}:5432/{settings.db_name}')
|
|
||||||
|
|
||||||
|
|
||||||
def get_session():
|
def get_session():
|
||||||
with Session(engine) as session:
|
with Session(engine) as session:
|
||||||
yield session
|
yield session
|
||||||
|
|
||||||
|
|
||||||
def create_all_tables():
|
def create_all_tables():
|
||||||
SQLModel.metadata.create_all(engine)
|
SQLModel.metadata.create_all(engine)
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
"""Forms module exceptions"""
|
|
||||||
import logging
|
|
||||||
|
|
||||||
class FormServiceError(Exception):
|
|
||||||
"""Form service exception"""
|
|
||||||
def __init__(self, message: str):
|
|
||||||
super().__init__(message)
|
|
||||||
logging.error('FormService : %s', message)
|
|
||||||
|
|
||||||
|
|
||||||
class UserNotFoundError(FormServiceError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ProductorNotFoundError(FormServiceError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class FormNotFoundError(FormServiceError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class FormCreateError(FormServiceError):
|
|
||||||
def __init__(self, message: str, field: str | None = None):
|
|
||||||
super().__init__(message)
|
|
||||||
self.field = field
|
|
||||||
@@ -1,108 +1,55 @@
|
|||||||
import src.forms.exceptions as exceptions
|
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||||
import src.forms.service as service
|
|
||||||
import src.messages as messages
|
import src.messages as messages
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
import src.models as models
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.database import get_session
|
from src.database import get_session
|
||||||
|
from sqlmodel import Session
|
||||||
|
import src.forms.service as service
|
||||||
|
from src.auth.auth import get_current_user
|
||||||
|
|
||||||
router = APIRouter(prefix='/forms')
|
router = APIRouter(prefix='/forms')
|
||||||
|
|
||||||
|
@router.get('/', response_model=list[models.FormPublic])
|
||||||
@router.get('', response_model=list[models.FormPublic])
|
|
||||||
async def get_forms(
|
async def get_forms(
|
||||||
seasons: list[str] = Query([]),
|
seasons: list[str] = Query([]),
|
||||||
productors: list[str] = Query([]),
|
productors: list[str] = Query([]),
|
||||||
current_season: bool = False,
|
current_season: bool = False,
|
||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
return service.get_all(session, seasons, productors, current_season)
|
return service.get_all(session, seasons, productors, current_season)
|
||||||
|
|
||||||
|
@router.get('/{id}', response_model=models.FormPublic)
|
||||||
@router.get('/referents', response_model=list[models.FormPublic])
|
async def get_form(id: int, session: Session = Depends(get_session)):
|
||||||
async def get_forms_filtered(
|
result = service.get_one(session, id)
|
||||||
seasons: list[str] = Query([]),
|
|
||||||
productors: list[str] = Query([]),
|
|
||||||
current_season: bool = False,
|
|
||||||
session: Session = Depends(get_session),
|
|
||||||
user: models.User = Depends(get_current_user)
|
|
||||||
):
|
|
||||||
return service.get_all(session, seasons, productors, current_season, user)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('/{_id}', response_model=models.FormPublic)
|
|
||||||
async def get_form(
|
|
||||||
_id: int,
|
|
||||||
session: Session = Depends(get_session)
|
|
||||||
):
|
|
||||||
result = service.get_one(session, _id)
|
|
||||||
if result is None:
|
if result is None:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
status_code=404,
|
|
||||||
detail=messages.Messages.not_found('form')
|
|
||||||
)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@router.post('/', response_model=models.FormPublic)
|
||||||
@router.post('', response_model=models.FormPublic)
|
|
||||||
async def create_form(
|
async def create_form(
|
||||||
form: models.FormCreate,
|
form: models.FormCreate,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
if not service.is_allowed(session, user, form=form):
|
return service.create_one(session, form)
|
||||||
raise HTTPException(
|
|
||||||
status_code=403,
|
|
||||||
detail=messages.Messages.not_allowed('forms', 'update')
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
form = service.create_one(session, form)
|
|
||||||
except exceptions.ProductorNotFoundError as error:
|
|
||||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
|
||||||
except exceptions.UserNotFoundError as error:
|
|
||||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
|
||||||
except exceptions.FormCreateError as error:
|
|
||||||
raise HTTPException(status_code=400, detail=str(error)) from error
|
|
||||||
return form
|
|
||||||
|
|
||||||
|
@router.put('/{id}', response_model=models.FormPublic)
|
||||||
@router.put('/{_id}', response_model=models.FormPublic)
|
|
||||||
async def update_form(
|
async def update_form(
|
||||||
_id: int,
|
id: int, form: models.FormUpdate,
|
||||||
form: models.FormUpdate,
|
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
if not service.is_allowed(session, user, _id=_id):
|
result = service.update_one(session, id, form)
|
||||||
raise HTTPException(
|
if result is None:
|
||||||
status_code=403,
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
detail=messages.Messages.not_allowed('forms', 'update')
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
result = service.update_one(session, _id, form)
|
|
||||||
except exceptions.FormNotFoundError as error:
|
|
||||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
|
||||||
except exceptions.ProductorNotFoundError as error:
|
|
||||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
|
||||||
except exceptions.UserNotFoundError as error:
|
|
||||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@router.delete('/{id}', response_model=models.FormPublic)
|
||||||
@router.delete('/{_id}', response_model=models.FormPublic)
|
|
||||||
async def delete_form(
|
async def delete_form(
|
||||||
_id: int,
|
id: int,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
if not service.is_allowed(session, user, _id=_id):
|
result = service.delete_one(session, id)
|
||||||
raise HTTPException(
|
if result is None:
|
||||||
status_code=403,
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
detail=messages.Messages.not_allowed('forms', 'delete')
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
result = service.delete_one(session, _id)
|
|
||||||
except exceptions.FormNotFoundError as error:
|
|
||||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1,69 +1,41 @@
|
|||||||
import src.forms.exceptions as exceptions
|
|
||||||
import src.messages as messages
|
|
||||||
from sqlalchemy import func
|
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src import models
|
import src.models as models
|
||||||
|
from sqlalchemy import func
|
||||||
|
|
||||||
def get_all(
|
def get_all(
|
||||||
session: Session,
|
session: Session,
|
||||||
seasons: list[str],
|
seasons: list[str],
|
||||||
productors: list[str],
|
productors: list[str],
|
||||||
current_season: bool,
|
current_season: bool,
|
||||||
user: models.User = None
|
|
||||||
) -> list[models.FormPublic]:
|
) -> list[models.FormPublic]:
|
||||||
statement = select(models.Form)
|
statement = select(models.Form)
|
||||||
if user:
|
|
||||||
statement = statement .join(
|
|
||||||
models.Productor,
|
|
||||||
models.Form.productor_id == models.Productor.id) .where(
|
|
||||||
models.Productor.type.in_(
|
|
||||||
[
|
|
||||||
r.name for r in user.roles])) .distinct()
|
|
||||||
if len(seasons) > 0:
|
if len(seasons) > 0:
|
||||||
statement = statement.where(models.Form.season.in_(seasons))
|
statement = statement.where(models.Form.season.in_(seasons))
|
||||||
if len(productors) > 0:
|
if len(productors) > 0:
|
||||||
statement = statement.join(
|
statement = statement.join(models.Productor).where(models.Productor.name.in_(productors))
|
||||||
models.Productor).where(
|
|
||||||
models.Productor.name.in_(productors))
|
|
||||||
if not user:
|
|
||||||
statement = statement.where(models.Form.visible)
|
|
||||||
if current_season:
|
if current_season:
|
||||||
subquery = (
|
subquery = (
|
||||||
select(
|
select(
|
||||||
models.Productor.type,
|
models.Productor.type,
|
||||||
func.max(models.Form.start).label("max_start")
|
func.max(models.Form.start).label("max_start")
|
||||||
)
|
)
|
||||||
.join(models.Form)
|
.join(models.Form)\
|
||||||
.group_by(models.Productor.type)
|
.group_by(models.Productor.type)\
|
||||||
.subquery()
|
.subquery()
|
||||||
)
|
)
|
||||||
statement = select(models.Form)\
|
statement = select(models.Form)\
|
||||||
.join(models.Productor)\
|
.join(models.Productor)\
|
||||||
.join(subquery,
|
.join(subquery,
|
||||||
(models.Productor.type == subquery.c.type) &
|
(models.Productor.type == subquery.c.type) &
|
||||||
(models.Form.start == subquery.c.max_start)
|
(models.Form.start == subquery.c.max_start)
|
||||||
)
|
)
|
||||||
if not user:
|
|
||||||
statement = statement.where(models.Form.visible)
|
|
||||||
return session.exec(statement.order_by(models.Form.name)).all()
|
return session.exec(statement.order_by(models.Form.name)).all()
|
||||||
return session.exec(statement.order_by(models.Form.name)).all()
|
return session.exec(statement.order_by(models.Form.name)).all()
|
||||||
|
|
||||||
|
|
||||||
def get_one(session: Session, form_id: int) -> models.FormPublic:
|
def get_one(session: Session, form_id: int) -> models.FormPublic:
|
||||||
return session.get(models.Form, form_id)
|
return session.get(models.Form, form_id)
|
||||||
|
|
||||||
|
|
||||||
def create_one(session: Session, form: models.FormCreate) -> models.FormPublic:
|
def create_one(session: Session, form: models.FormCreate) -> models.FormPublic:
|
||||||
if not form:
|
|
||||||
raise exceptions.FormCreateError(
|
|
||||||
messages.Messages.invalid_input(
|
|
||||||
'form', 'input cannot be None'))
|
|
||||||
if not session.get(models.Productor, form.productor_id):
|
|
||||||
raise exceptions.ProductorNotFoundError(
|
|
||||||
messages.Messages.not_found('productor'))
|
|
||||||
if not session.get(models.User, form.referer_id):
|
|
||||||
raise exceptions.UserNotFoundError(messages.Messages.not_found('user'))
|
|
||||||
form_create = form.model_dump(exclude_unset=True)
|
form_create = form.model_dump(exclude_unset=True)
|
||||||
new_form = models.Form(**form_create)
|
new_form = models.Form(**form_create)
|
||||||
session.add(new_form)
|
session.add(new_form)
|
||||||
@@ -71,22 +43,12 @@ def create_one(session: Session, form: models.FormCreate) -> models.FormPublic:
|
|||||||
session.refresh(new_form)
|
session.refresh(new_form)
|
||||||
return new_form
|
return new_form
|
||||||
|
|
||||||
|
def update_one(session: Session, id: int, form: models.FormUpdate) -> models.FormPublic:
|
||||||
def update_one(
|
statement = select(models.Form).where(models.Form.id == id)
|
||||||
session: Session,
|
|
||||||
_id: int,
|
|
||||||
form: models.FormUpdate) -> models.FormPublic:
|
|
||||||
statement = select(models.Form).where(models.Form.id == _id)
|
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
new_form = result.first()
|
new_form = result.first()
|
||||||
if not new_form:
|
if not new_form:
|
||||||
raise exceptions.FormNotFoundError(messages.Messages.not_found('form'))
|
return None
|
||||||
if form.productor_id and not session.get(
|
|
||||||
models.Productor, form.productor_id):
|
|
||||||
raise exceptions.ProductorNotFoundError(
|
|
||||||
messages.Messages.not_found('productor'))
|
|
||||||
if form.referer_id and not session.get(models.User, form.referer_id):
|
|
||||||
raise exceptions.UserNotFoundError(messages.Messages.not_found('user'))
|
|
||||||
form_updates = form.model_dump(exclude_unset=True)
|
form_updates = form.model_dump(exclude_unset=True)
|
||||||
for key, value in form_updates.items():
|
for key, value in form_updates.items():
|
||||||
setattr(new_form, key, value)
|
setattr(new_form, key, value)
|
||||||
@@ -95,44 +57,21 @@ def update_one(
|
|||||||
session.refresh(new_form)
|
session.refresh(new_form)
|
||||||
return new_form
|
return new_form
|
||||||
|
|
||||||
|
def delete_one(session: Session, id: int) -> models.FormPublic:
|
||||||
def delete_one(session: Session, _id: int) -> models.FormPublic:
|
statement = select(models.Form).where(models.Form.id == id)
|
||||||
statement = select(models.Form).where(models.Form.id == _id)
|
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
form = result.first()
|
form = result.first()
|
||||||
if not form:
|
if not form:
|
||||||
raise exceptions.FormNotFoundError(messages.Messages.not_found('form'))
|
return None
|
||||||
result = models.FormPublic.model_validate(form)
|
result = models.FormPublic.model_validate(form)
|
||||||
session.delete(form)
|
session.delete(form)
|
||||||
session.commit()
|
session.commit()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def is_allowed(session: Session, user: models.User, id: int) -> bool:
|
||||||
def is_allowed(
|
statement = select(models.Form)\
|
||||||
session: Session,
|
.join(models.Productor, models.Form.productor_id == models.Productor.id)\
|
||||||
user: models.User,
|
.where(models.Form.id == id)\
|
||||||
_id: int = None,
|
.where(models.Productor.type.in_([r.name for r in user.roles]))\
|
||||||
form: models.FormCreate = None
|
|
||||||
) -> bool:
|
|
||||||
if not _id:
|
|
||||||
statement = (
|
|
||||||
select(models.Productor)
|
|
||||||
.where(models.Productor.id == form.productor_id)
|
|
||||||
)
|
|
||||||
productor = session.exec(statement).first()
|
|
||||||
return productor.type in [r.name for r in user.roles]
|
|
||||||
statement = (
|
|
||||||
select(models.Form)
|
|
||||||
.join(
|
|
||||||
models.Productor,
|
|
||||||
models.Form.productor_id == models.Productor.id
|
|
||||||
)
|
|
||||||
.where(models.Form.id == _id)
|
|
||||||
.where(
|
|
||||||
models.Productor.type.in_(
|
|
||||||
[r.name for r in user.roles]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.distinct()
|
.distinct()
|
||||||
)
|
|
||||||
return len(session.exec(statement).all()) > 0
|
return len(session.exec(statement).all()) > 0
|
||||||
@@ -1,18 +1,21 @@
|
|||||||
|
from sqlmodel import SQLModel
|
||||||
|
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from src.auth.auth import router as auth_router
|
|
||||||
|
from src.templates.templates import router as template_router
|
||||||
from src.contracts.contracts import router as contracts_router
|
from src.contracts.contracts import router as contracts_router
|
||||||
from src.forms.forms import router as forms_router
|
from src.forms.forms import router as forms_router
|
||||||
from src.productors.productors import router as productors_router
|
from src.productors.productors import router as productors_router
|
||||||
from src.products.products import router as products_router
|
from src.products.products import router as products_router
|
||||||
from src.settings import settings
|
|
||||||
from src.shipments.shipments import router as shipment_router
|
|
||||||
from src.templates.templates import router as template_router
|
|
||||||
from src.users.users import router as users_router
|
from src.users.users import router as users_router
|
||||||
|
from src.auth.auth import router as auth_router
|
||||||
|
from src.shipments.shipments import router as shipment_router
|
||||||
|
from src.settings import settings
|
||||||
|
from src.database import engine
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
allow_origins=[
|
allow_origins=[
|
||||||
@@ -24,11 +27,14 @@ app.add_middleware(
|
|||||||
expose_headers=['x-nbpage', 'Content-Disposition']
|
expose_headers=['x-nbpage', 'Content-Disposition']
|
||||||
)
|
)
|
||||||
|
|
||||||
app.include_router(template_router, prefix="/api")
|
|
||||||
app.include_router(contracts_router, prefix="/api")
|
app.include_router(template_router)
|
||||||
app.include_router(forms_router, prefix="/api")
|
app.include_router(contracts_router)
|
||||||
app.include_router(productors_router, prefix="/api")
|
app.include_router(forms_router)
|
||||||
app.include_router(products_router, prefix="/api")
|
app.include_router(productors_router)
|
||||||
app.include_router(users_router, prefix="/api")
|
app.include_router(products_router)
|
||||||
app.include_router(auth_router, prefix="/api")
|
app.include_router(users_router)
|
||||||
app.include_router(shipment_router, prefix="/api")
|
app.include_router(auth_router)
|
||||||
|
app.include_router(shipment_router)
|
||||||
|
|
||||||
|
SQLModel.metadata.create_all(engine)
|
||||||
@@ -1,20 +1,10 @@
|
|||||||
pdferror = 'An error occured during PDF generation please contact administrator'
|
notfound = "Resource was not found."
|
||||||
|
pdferror = "An error occured during PDF generation please contact administrator"
|
||||||
|
tokenexipired = "Token expired"
|
||||||
class Messages:
|
invalidtoken = "Invalid token"
|
||||||
unauthorized = 'User is Unauthorized'
|
notauthenticated = "Not authenticated"
|
||||||
notauthenticated = 'User is not authenticated'
|
usernotfound = "User not found"
|
||||||
tokenexipired = 'Token has expired'
|
userloggedout = "User logged out"
|
||||||
invalidtoken = 'Token is invalid'
|
failtogettoken = "Failed to get token"
|
||||||
|
unauthorized = "Unauthorized"
|
||||||
@staticmethod
|
notallowed = "Not Allowed"
|
||||||
def not_found(resource: str) -> str:
|
|
||||||
return f'{resource.capitalize()} not found'
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def invalid_input(resource: str, reason: str = "") -> str:
|
|
||||||
return f'Invalid {resource} input {':' if reason else ""} {reason}'
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def not_allowed(resource: str, action: str) -> str:
|
|
||||||
return f'User is not allowed to {action} this {resource}'
|
|
||||||
@@ -1,136 +1,98 @@
|
|||||||
import datetime
|
from sqlmodel import Field, SQLModel, Relationship, Column, LargeBinary
|
||||||
from enum import StrEnum
|
from enum import StrEnum
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
import datetime
|
||||||
from sqlmodel import Column, Field, LargeBinary, Relationship, SQLModel
|
|
||||||
|
|
||||||
|
|
||||||
class ContractType(SQLModel, table=True):
|
class ContractType(SQLModel, table=True):
|
||||||
id: int | None = Field(
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
default=None,
|
|
||||||
primary_key=True
|
|
||||||
)
|
|
||||||
name: str
|
name: str
|
||||||
|
|
||||||
|
|
||||||
class UserContractTypeLink(SQLModel, table=True):
|
class UserContractTypeLink(SQLModel, table=True):
|
||||||
user_id: int = Field(
|
user_id: int = Field(foreign_key="user.id", primary_key=True)
|
||||||
foreign_key='user.id',
|
contract_type_id: int = Field(foreign_key="contracttype.id", primary_key=True)
|
||||||
primary_key=True
|
|
||||||
)
|
|
||||||
contract_type_id: int = Field(
|
|
||||||
foreign_key='contracttype.id',
|
|
||||||
primary_key=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class UserBase(SQLModel):
|
class UserBase(SQLModel):
|
||||||
name: str
|
name: str
|
||||||
email: str
|
email: str
|
||||||
|
|
||||||
|
|
||||||
class UserPublic(UserBase):
|
class UserPublic(UserBase):
|
||||||
id: int
|
id: int
|
||||||
roles: list[ContractType]
|
roles: list[ContractType]
|
||||||
|
|
||||||
|
|
||||||
class User(UserBase, table=True):
|
class User(UserBase, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
roles: list[ContractType] = Relationship(
|
roles: list[ContractType] = Relationship(
|
||||||
link_model=UserContractTypeLink
|
link_model=UserContractTypeLink
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserUpdate(SQLModel):
|
class UserUpdate(SQLModel):
|
||||||
name: str | None
|
name: str | None
|
||||||
email: str | None
|
email: str | None
|
||||||
role_names: list[str] | None
|
role_names: list[str] | None
|
||||||
|
|
||||||
|
|
||||||
class UserCreate(UserBase):
|
class UserCreate(UserBase):
|
||||||
role_names: list[str] | None
|
role_names: list[str] | None
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethodBase(SQLModel):
|
class PaymentMethodBase(SQLModel):
|
||||||
name: str
|
name: str
|
||||||
details: str
|
details: str
|
||||||
max: int | None
|
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethod(PaymentMethodBase, table=True):
|
class PaymentMethod(PaymentMethodBase, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
productor_id: int = Field(foreign_key='productor.id', ondelete='CASCADE')
|
productor_id: int = Field(foreign_key="productor.id", ondelete="CASCADE")
|
||||||
productor: Optional['Productor'] = Relationship(
|
productor: Optional["Productor"] = Relationship(
|
||||||
back_populates='payment_methods',
|
back_populates="payment_methods",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PaymentMethodPublic(PaymentMethodBase):
|
class PaymentMethodPublic(PaymentMethodBase):
|
||||||
id: int
|
id: int
|
||||||
productor: Optional['Productor']
|
productor: Optional["Productor"]
|
||||||
|
|
||||||
|
|
||||||
class ProductorBase(SQLModel):
|
class ProductorBase(SQLModel):
|
||||||
name: str
|
name: str
|
||||||
address: str
|
address: str
|
||||||
type: str
|
type: str
|
||||||
|
|
||||||
|
|
||||||
class ProductorPublic(ProductorBase):
|
class ProductorPublic(ProductorBase):
|
||||||
id: int
|
id: int
|
||||||
products: list['Product'] = Field(default_factory=list)
|
products: list["Product"] = []
|
||||||
payment_methods: list['PaymentMethod'] = Field(default_factory=list)
|
payment_methods: list["PaymentMethod"] = []
|
||||||
|
|
||||||
|
|
||||||
class Productor(ProductorBase, table=True):
|
class Productor(ProductorBase, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
|
|
||||||
products: list['Product'] = Relationship(
|
products: list["Product"] = Relationship(
|
||||||
back_populates='productor',
|
back_populates='productor',
|
||||||
sa_relationship_kwargs={
|
sa_relationship_kwargs={
|
||||||
'order_by': 'Product.name'
|
"order_by": "Product.name"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
payment_methods: list['PaymentMethod'] = Relationship(
|
payment_methods: list["PaymentMethod"] = Relationship(
|
||||||
back_populates='productor',
|
back_populates="productor",
|
||||||
cascade_delete=True
|
cascade_delete=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ProductorUpdate(SQLModel):
|
class ProductorUpdate(SQLModel):
|
||||||
name: str | None
|
name: str | None
|
||||||
address: str | None
|
address: str | None
|
||||||
payment_methods: list['PaymentMethod'] = Field(default_factory=list)
|
payment_methods: list["PaymentMethod"] = []
|
||||||
type: str | None
|
type: str | None
|
||||||
|
|
||||||
|
|
||||||
class ProductorCreate(ProductorBase):
|
class ProductorCreate(ProductorBase):
|
||||||
payment_methods: list['PaymentMethod'] = Field(default_factory=list)
|
payment_methods: list["PaymentMethod"] = []
|
||||||
|
|
||||||
|
|
||||||
class Unit(StrEnum):
|
class Unit(StrEnum):
|
||||||
GRAMS = '1'
|
GRAMS = "1"
|
||||||
KILO = '2'
|
KILO = "2"
|
||||||
PIECE = '3'
|
PIECE = "3"
|
||||||
|
|
||||||
|
|
||||||
class ProductType(StrEnum):
|
class ProductType(StrEnum):
|
||||||
OCCASIONAL = '1'
|
OCCASIONAL = "1"
|
||||||
RECCURENT = '2'
|
RECCURENT = "2"
|
||||||
|
|
||||||
|
|
||||||
class ShipmentProductLink(SQLModel, table=True):
|
class ShipmentProductLink(SQLModel, table=True):
|
||||||
shipment_id: Optional[int] = Field(
|
shipment_id: Optional[int] = Field(default=None, foreign_key="shipment.id", primary_key=True)
|
||||||
default=None,
|
product_id: Optional[int] = Field(default=None, foreign_key="product.id", primary_key=True)
|
||||||
foreign_key='shipment.id',
|
|
||||||
primary_key=True
|
|
||||||
)
|
|
||||||
product_id: Optional[int] = Field(
|
|
||||||
default=None,
|
|
||||||
foreign_key='product.id',
|
|
||||||
primary_key=True
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ProductBase(SQLModel):
|
class ProductBase(SQLModel):
|
||||||
name: str
|
name: str
|
||||||
@@ -140,31 +102,17 @@ class ProductBase(SQLModel):
|
|||||||
quantity: float | None
|
quantity: float | None
|
||||||
quantity_unit: str | None
|
quantity_unit: str | None
|
||||||
type: ProductType
|
type: ProductType
|
||||||
productor_id: int | None = Field(
|
productor_id: int | None = Field(default=None, foreign_key="productor.id")
|
||||||
default=None,
|
|
||||||
foreign_key='productor.id'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ProductPublic(ProductBase):
|
class ProductPublic(ProductBase):
|
||||||
id: int
|
id: int
|
||||||
productor: Productor | None
|
productor: Productor | None
|
||||||
shipments: list['Shipment'] | None
|
shipments: list["Shipment"] | None
|
||||||
|
|
||||||
|
|
||||||
class Product(ProductBase, table=True):
|
class Product(ProductBase, table=True):
|
||||||
id: int | None = Field(
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
default=None,
|
shipments: list["Shipment"] = Relationship(back_populates="products", link_model=ShipmentProductLink)
|
||||||
primary_key=True
|
productor: Optional[Productor] = Relationship(back_populates="products")
|
||||||
)
|
|
||||||
shipments: list['Shipment'] = Relationship(
|
|
||||||
back_populates='products',
|
|
||||||
link_model=ShipmentProductLink
|
|
||||||
)
|
|
||||||
productor: Optional[Productor] = Relationship(
|
|
||||||
back_populates='products'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ProductUpdate(SQLModel):
|
class ProductUpdate(SQLModel):
|
||||||
name: str | None
|
name: str | None
|
||||||
@@ -176,46 +124,40 @@ class ProductUpdate(SQLModel):
|
|||||||
productor_id: int | None
|
productor_id: int | None
|
||||||
type: ProductType | None
|
type: ProductType | None
|
||||||
|
|
||||||
|
|
||||||
class ProductCreate(ProductBase):
|
class ProductCreate(ProductBase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class FormBase(SQLModel):
|
class FormBase(SQLModel):
|
||||||
name: str
|
name: str
|
||||||
productor_id: int | None = Field(default=None, foreign_key='productor.id')
|
productor_id: int | None = Field(default=None, foreign_key="productor.id")
|
||||||
referer_id: int | None = Field(default=None, foreign_key='user.id')
|
referer_id: int | None = Field(default=None, foreign_key="user.id")
|
||||||
season: str
|
season: str
|
||||||
start: datetime.date
|
start: datetime.date
|
||||||
end: datetime.date
|
end: datetime.date
|
||||||
minimum_shipment_value: float | None
|
minimum_shipment_value: float | None
|
||||||
visible: bool
|
|
||||||
|
|
||||||
|
|
||||||
class FormPublic(FormBase):
|
class FormPublic(FormBase):
|
||||||
id: int
|
id: int
|
||||||
productor: ProductorPublic | None
|
productor: ProductorPublic | None
|
||||||
referer: User | None
|
referer: User | None
|
||||||
shipments: list['ShipmentPublic'] = Field(default_factory=list)
|
shipments: list["ShipmentPublic"] = []
|
||||||
|
|
||||||
|
|
||||||
class Form(FormBase, table=True):
|
class Form(FormBase, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
productor: Optional['Productor'] = Relationship()
|
productor: Optional['Productor'] = Relationship()
|
||||||
referer: Optional['User'] = Relationship()
|
referer: Optional['User'] = Relationship()
|
||||||
shipments: list['Shipment'] = Relationship(
|
shipments: list["Shipment"] = Relationship(
|
||||||
back_populates='form',
|
back_populates="form",
|
||||||
cascade_delete=True,
|
cascade_delete=True,
|
||||||
sa_relationship_kwargs={
|
sa_relationship_kwargs={
|
||||||
'order_by': 'Shipment.name'
|
"order_by": "Shipment.name"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
contracts: list['Contract'] = Relationship(
|
contracts: list["Contract"] = Relationship(
|
||||||
back_populates='form',
|
back_populates="form",
|
||||||
cascade_delete=True
|
cascade_delete=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class FormUpdate(SQLModel):
|
class FormUpdate(SQLModel):
|
||||||
name: str | None
|
name: str | None
|
||||||
productor_id: int | None
|
productor_id: int | None
|
||||||
@@ -224,46 +166,36 @@ class FormUpdate(SQLModel):
|
|||||||
start: datetime.date | None
|
start: datetime.date | None
|
||||||
end: datetime.date | None
|
end: datetime.date | None
|
||||||
minimum_shipment_value: float | None
|
minimum_shipment_value: float | None
|
||||||
visible: bool | None
|
|
||||||
|
|
||||||
|
|
||||||
class FormCreate(FormBase):
|
class FormCreate(FormBase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TemplateBase(SQLModel):
|
class TemplateBase(SQLModel):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TemplatePublic(TemplateBase):
|
class TemplatePublic(TemplateBase):
|
||||||
id: int
|
id: int
|
||||||
|
|
||||||
|
|
||||||
class Template(TemplateBase, table=True):
|
class Template(TemplateBase, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
|
|
||||||
|
|
||||||
class TemplateUpdate(SQLModel):
|
class TemplateUpdate(SQLModel):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class TemplateCreate(TemplateBase):
|
class TemplateCreate(TemplateBase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ChequeBase(SQLModel):
|
class ChequeBase(SQLModel):
|
||||||
name: str
|
name: str
|
||||||
value: str
|
value: str
|
||||||
|
|
||||||
|
|
||||||
class Cheque(ChequeBase, table=True):
|
class Cheque(ChequeBase, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
contract_id: int = Field(foreign_key='contract.id', ondelete='CASCADE')
|
contract_id: int = Field(foreign_key="contract.id", ondelete="CASCADE")
|
||||||
contract: Optional['Contract'] = Relationship(
|
contract: Optional["Contract"] = Relationship(
|
||||||
back_populates='cheques',
|
back_populates="cheques",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ContractBase(SQLModel):
|
class ContractBase(SQLModel):
|
||||||
firstname: str
|
firstname: str
|
||||||
lastname: str
|
lastname: str
|
||||||
@@ -272,122 +204,105 @@ class ContractBase(SQLModel):
|
|||||||
payment_method: str
|
payment_method: str
|
||||||
cheque_quantity: int
|
cheque_quantity: int
|
||||||
|
|
||||||
|
|
||||||
class Contract(ContractBase, table=True):
|
class Contract(ContractBase, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
|
|
||||||
form_id: int = Field(
|
form_id: int = Field(
|
||||||
foreign_key='form.id',
|
foreign_key="form.id",
|
||||||
nullable=False,
|
nullable=False,
|
||||||
ondelete='CASCADE'
|
ondelete="CASCADE"
|
||||||
)
|
)
|
||||||
products: list['ContractProduct'] = Relationship(
|
products: list["ContractProduct"] = Relationship(
|
||||||
back_populates='contract',
|
back_populates="contract",
|
||||||
cascade_delete=True
|
cascade_delete=True
|
||||||
)
|
)
|
||||||
form: Form = Relationship(back_populates='contracts')
|
form: Optional[Form] = Relationship(back_populates="contracts")
|
||||||
cheques: list[Cheque] = Relationship(
|
cheques: list[Cheque] = Relationship(
|
||||||
back_populates='contract',
|
back_populates="contract",
|
||||||
cascade_delete=True
|
cascade_delete=True
|
||||||
)
|
)
|
||||||
file: bytes = Field(sa_column=Column(LargeBinary))
|
file: bytes = Field(sa_column=Column(LargeBinary))
|
||||||
total_price: float | None
|
total_price: float | None
|
||||||
|
|
||||||
|
|
||||||
class ContractCreate(ContractBase):
|
class ContractCreate(ContractBase):
|
||||||
products: list['ContractProductCreate'] = Field(default_factory=list)
|
products: list["ContractProductCreate"] = []
|
||||||
cheques: list['Cheque'] = Field(default_factory=list)
|
cheques: list["Cheque"] = []
|
||||||
form_id: int
|
form_id: int
|
||||||
|
|
||||||
|
|
||||||
class ContractUpdate(SQLModel):
|
class ContractUpdate(SQLModel):
|
||||||
file: bytes
|
file: bytes
|
||||||
|
|
||||||
|
|
||||||
class ContractPublic(ContractBase):
|
class ContractPublic(ContractBase):
|
||||||
id: int
|
id: int
|
||||||
products: list['ContractProduct'] = Field(default_factory=list)
|
products: list["ContractProduct"] = []
|
||||||
form: Form
|
form: Form
|
||||||
total_price: float | None
|
total_price: float | None
|
||||||
# file: bytes
|
# file: bytes
|
||||||
|
|
||||||
|
|
||||||
class ContractProductBase(SQLModel):
|
class ContractProductBase(SQLModel):
|
||||||
product_id: int = Field(
|
product_id: int = Field(
|
||||||
foreign_key='product.id',
|
foreign_key="product.id",
|
||||||
nullable=False,
|
nullable=False,
|
||||||
ondelete='CASCADE'
|
ondelete="CASCADE"
|
||||||
)
|
)
|
||||||
shipment_id: int | None = Field(
|
shipment_id: int | None = Field(
|
||||||
default=None,
|
default=None,
|
||||||
foreign_key='shipment.id',
|
foreign_key="shipment.id",
|
||||||
nullable=True,
|
nullable=True,
|
||||||
ondelete='CASCADE'
|
ondelete="CASCADE"
|
||||||
)
|
)
|
||||||
quantity: float
|
quantity: float
|
||||||
|
|
||||||
|
|
||||||
class ContractProduct(ContractProductBase, table=True):
|
class ContractProduct(ContractProductBase, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
contract_id: int = Field(
|
contract_id: int = Field(
|
||||||
foreign_key='contract.id',
|
foreign_key="contract.id",
|
||||||
nullable=False,
|
nullable=False,
|
||||||
ondelete='CASCADE'
|
ondelete="CASCADE"
|
||||||
)
|
)
|
||||||
contract: Optional['Contract'] = Relationship(back_populates='products')
|
contract: Optional["Contract"] = Relationship(back_populates="products")
|
||||||
product: Optional['Product'] = Relationship()
|
product: Optional["Product"] = Relationship()
|
||||||
shipment: Optional['Shipment'] = Relationship()
|
shipment: Optional["Shipment"] = Relationship()
|
||||||
|
|
||||||
|
|
||||||
class ContractProductPublic(ContractProductBase):
|
class ContractProductPublic(ContractProductBase):
|
||||||
id: int
|
id: int
|
||||||
quantity: float
|
quantity: float
|
||||||
contract: Contract
|
contract: Contract
|
||||||
product: Product
|
product: Product
|
||||||
shipment: Optional['Shipment']
|
shipment: Optional["Shipment"]
|
||||||
|
|
||||||
|
|
||||||
class ContractProductCreate(ContractProductBase):
|
class ContractProductCreate(ContractProductBase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ContractProductUpdate(ContractProductBase):
|
class ContractProductUpdate(ContractProductBase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ShipmentBase(SQLModel):
|
class ShipmentBase(SQLModel):
|
||||||
name: str
|
name: str
|
||||||
date: datetime.date
|
date: datetime.date
|
||||||
form_id: int | None = Field(
|
form_id: int | None = Field(default=None, foreign_key="form.id", ondelete="CASCADE")
|
||||||
default=None,
|
|
||||||
foreign_key='form.id',
|
|
||||||
ondelete='CASCADE')
|
|
||||||
|
|
||||||
|
|
||||||
class ShipmentPublic(ShipmentBase):
|
class ShipmentPublic(ShipmentBase):
|
||||||
id: int
|
id: int
|
||||||
products: list[Product] = Field(default_factory=list)
|
products: list[Product] = []
|
||||||
form: Form | None
|
form: Form | None
|
||||||
|
|
||||||
|
|
||||||
class Shipment(ShipmentBase, table=True):
|
class Shipment(ShipmentBase, table=True):
|
||||||
id: int | None = Field(default=None, primary_key=True)
|
id: int | None = Field(default=None, primary_key=True)
|
||||||
products: list[Product] = Relationship(
|
products: list[Product] = Relationship(
|
||||||
back_populates='shipments',
|
back_populates="shipments",
|
||||||
link_model=ShipmentProductLink,
|
link_model=ShipmentProductLink,
|
||||||
sa_relationship_kwargs={
|
sa_relationship_kwargs={
|
||||||
'order_by': 'Product.name'
|
"order_by": "Product.name"
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
form: Optional[Form] = Relationship(back_populates='shipments')
|
form: Optional[Form] = Relationship(back_populates="shipments")
|
||||||
|
|
||||||
|
|
||||||
class ShipmentUpdate(SQLModel):
|
class ShipmentUpdate(SQLModel):
|
||||||
name: str | None
|
name: str | None
|
||||||
date: datetime.date | None
|
date: str | None
|
||||||
product_ids: list[int] | None = Field(default_factory=list)
|
product_ids: list[int] | None = []
|
||||||
|
|
||||||
|
|
||||||
class ShipmentCreate(ShipmentBase):
|
class ShipmentCreate(ShipmentBase):
|
||||||
product_ids: list[int] = Field(default_factory=list)
|
product_ids: list[int] = []
|
||||||
form_id: int
|
form_id: int
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
|
|
||||||
class ProductorServiceError(Exception):
|
|
||||||
def __init__(self, message: str):
|
|
||||||
super().__init__(message)
|
|
||||||
logging.error('ProductorService : %s', message)
|
|
||||||
|
|
||||||
|
|
||||||
class ProductorNotFoundError(ProductorServiceError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ProductorCreateError(ProductorServiceError):
|
|
||||||
def __init__(self, message: str, field: str | None = None):
|
|
||||||
super().__init__(message)
|
|
||||||
self.field = field
|
|
||||||
@@ -1,74 +1,59 @@
|
|||||||
|
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||||
import src.messages as messages
|
import src.messages as messages
|
||||||
import src.productors.exceptions as exceptions
|
import src.models as models
|
||||||
import src.productors.service as service
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.database import get_session
|
from src.database import get_session
|
||||||
|
from sqlmodel import Session
|
||||||
|
import src.productors.service as service
|
||||||
|
from src.auth.auth import get_current_user
|
||||||
|
|
||||||
router = APIRouter(prefix='/productors')
|
router = APIRouter(prefix='/productors')
|
||||||
|
|
||||||
|
@router.get('/', response_model=list[models.ProductorPublic])
|
||||||
@router.get('', response_model=list[models.ProductorPublic])
|
|
||||||
def get_productors(
|
def get_productors(
|
||||||
names: list[str] = Query([]),
|
names: list[str] = Query([]),
|
||||||
types: list[str] = Query([]),
|
types: list[str] = Query([]),
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
return service.get_all(session, user, names, types)
|
return service.get_all(session, names, types)
|
||||||
|
|
||||||
|
@router.get('/{id}', response_model=models.ProductorPublic)
|
||||||
@router.get('/{_id}', response_model=models.ProductorPublic)
|
|
||||||
def get_productor(
|
def get_productor(
|
||||||
_id: int,
|
id: int,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
result = service.get_one(session, _id)
|
result = service.get_one(session, id)
|
||||||
if result is None:
|
if result is None:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
status_code=404,
|
|
||||||
detail=messages.Messages.not_found('productor')
|
|
||||||
)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@router.post('/', response_model=models.ProductorPublic)
|
||||||
@router.post('', response_model=models.ProductorPublic)
|
|
||||||
def create_productor(
|
def create_productor(
|
||||||
productor: models.ProductorCreate,
|
productor: models.ProductorCreate,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
return service.create_one(session, productor)
|
||||||
result = service.create_one(session, productor)
|
|
||||||
except exceptions.ProductorCreateError as error:
|
|
||||||
raise HTTPException(status_code=400, detail=str(error)) from error
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
@router.put('/{id}', response_model=models.ProductorPublic)
|
||||||
@router.put('/{_id}', response_model=models.ProductorPublic)
|
|
||||||
def update_productor(
|
def update_productor(
|
||||||
_id: int, productor: models.ProductorUpdate,
|
id: int, productor: models.ProductorUpdate,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
result = service.update_one(session, id, productor)
|
||||||
result = service.update_one(session, _id, productor)
|
if result is None:
|
||||||
except exceptions.ProductorNotFoundError as error:
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@router.delete('/{id}', response_model=models.ProductorPublic)
|
||||||
@router.delete('/{_id}', response_model=models.ProductorPublic)
|
|
||||||
def delete_productor(
|
def delete_productor(
|
||||||
_id: int,
|
id: int,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
result = service.delete_one(session, id)
|
||||||
result = service.delete_one(session, _id)
|
if result is None:
|
||||||
except exceptions.ProductorNotFoundError as error:
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1,38 +1,23 @@
|
|||||||
import src.messages as messages
|
|
||||||
import src.productors.exceptions as exceptions
|
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src import models
|
import src.models as models
|
||||||
|
|
||||||
|
|
||||||
def get_all(
|
def get_all(
|
||||||
session: Session,
|
session: Session,
|
||||||
user: models.User,
|
|
||||||
names: list[str],
|
names: list[str],
|
||||||
types: list[str]
|
types: list[str]
|
||||||
) -> list[models.ProductorPublic]:
|
) -> list[models.ProductorPublic]:
|
||||||
statement = select(models.Productor)\
|
statement = select(models.Productor)
|
||||||
.where(models.Productor.type.in_([r.name for r in user.roles]))\
|
|
||||||
.distinct()
|
|
||||||
if len(names) > 0:
|
if len(names) > 0:
|
||||||
statement = statement.where(models.Productor.name.in_(names))
|
statement = statement.where(models.Productor.name.in_(names))
|
||||||
if len(types) > 0:
|
if len(types) > 0:
|
||||||
statement = statement.where(models.Productor.type.in_(types))
|
statement = statement.where(models.Productor.type.in_(types))
|
||||||
return session.exec(statement.order_by(models.Productor.name)).all()
|
return session.exec(statement.order_by(models.Productor.name)).all()
|
||||||
|
|
||||||
|
|
||||||
def get_one(session: Session, productor_id: int) -> models.ProductorPublic:
|
def get_one(session: Session, productor_id: int) -> models.ProductorPublic:
|
||||||
return session.get(models.Productor, productor_id)
|
return session.get(models.Productor, productor_id)
|
||||||
|
|
||||||
|
def create_one(session: Session, productor: models.ProductorCreate) -> models.ProductorPublic:
|
||||||
def create_one(
|
productor_create = productor.model_dump(exclude_unset=True, exclude="payment_methods")
|
||||||
session: Session,
|
|
||||||
productor: models.ProductorCreate) -> models.ProductorPublic:
|
|
||||||
if not productor:
|
|
||||||
raise exceptions.ProductorCreateError(
|
|
||||||
messages.Messages.invalid_input(
|
|
||||||
'productor', 'input cannot be None'))
|
|
||||||
productor_create = productor.model_dump(
|
|
||||||
exclude_unset=True, exclude='payment_methods')
|
|
||||||
new_productor = models.Productor(**productor_create)
|
new_productor = models.Productor(**productor_create)
|
||||||
|
|
||||||
new_productor.payment_methods = [
|
new_productor.payment_methods = [
|
||||||
@@ -47,31 +32,25 @@ def create_one(
|
|||||||
session.refresh(new_productor)
|
session.refresh(new_productor)
|
||||||
return new_productor
|
return new_productor
|
||||||
|
|
||||||
|
def update_one(session: Session, id: int, productor: models.ProductorUpdate) -> models.ProductorPublic:
|
||||||
def update_one(
|
|
||||||
session: Session,
|
|
||||||
id: int,
|
|
||||||
productor: models.ProductorUpdate) -> models.ProductorPublic:
|
|
||||||
statement = select(models.Productor).where(models.Productor.id == id)
|
statement = select(models.Productor).where(models.Productor.id == id)
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
new_productor = result.first()
|
new_productor = result.first()
|
||||||
if not new_productor:
|
if not new_productor:
|
||||||
raise exceptions.ProductorNotFoundError(
|
return None
|
||||||
messages.Messages.not_found('productor'))
|
|
||||||
|
|
||||||
productor_updates = productor.model_dump(exclude_unset=True)
|
productor_updates = productor.model_dump(exclude_unset=True)
|
||||||
if 'payment_methods' in productor_updates:
|
if "payment_methods" in productor_updates:
|
||||||
new_productor.payment_methods.clear()
|
new_productor.payment_methods.clear()
|
||||||
for pm in productor_updates['payment_methods']:
|
for pm in productor_updates["payment_methods"]:
|
||||||
new_productor.payment_methods.append(
|
new_productor.payment_methods.append(
|
||||||
models.PaymentMethod(
|
models.PaymentMethod(
|
||||||
name=pm['name'],
|
name=pm["name"],
|
||||||
details=pm['details'],
|
details=pm["details"],
|
||||||
productor_id=id,
|
productor_id=id
|
||||||
max=pm['max']
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
del productor_updates['payment_methods']
|
del productor_updates["payment_methods"]
|
||||||
|
|
||||||
for key, value in productor_updates.items():
|
for key, value in productor_updates.items():
|
||||||
setattr(new_productor, key, value)
|
setattr(new_productor, key, value)
|
||||||
@@ -80,31 +59,13 @@ def update_one(
|
|||||||
session.refresh(new_productor)
|
session.refresh(new_productor)
|
||||||
return new_productor
|
return new_productor
|
||||||
|
|
||||||
|
|
||||||
def delete_one(session: Session, id: int) -> models.ProductorPublic:
|
def delete_one(session: Session, id: int) -> models.ProductorPublic:
|
||||||
statement = select(models.Productor).where(models.Productor.id == id)
|
statement = select(models.Productor).where(models.Productor.id == id)
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
productor = result.first()
|
productor = result.first()
|
||||||
if not productor:
|
if not productor:
|
||||||
raise exceptions.ProductorNotFoundError(
|
return None
|
||||||
messages.Messages.not_found('productor'))
|
|
||||||
result = models.ProductorPublic.model_validate(productor)
|
result = models.ProductorPublic.model_validate(productor)
|
||||||
session.delete(productor)
|
session.delete(productor)
|
||||||
session.commit()
|
session.commit()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def is_allowed(
|
|
||||||
session: Session,
|
|
||||||
user: models.User,
|
|
||||||
_id: int,
|
|
||||||
productor: models.ProductorCreate
|
|
||||||
) -> bool:
|
|
||||||
if not _id:
|
|
||||||
return productor.type in [r.name for r in user.roles]
|
|
||||||
statement = (
|
|
||||||
select(models.Productor)
|
|
||||||
.where(models.Productor.id == _id)
|
|
||||||
.where(models.Productor.type.in_([r.name for r in user.roles]))
|
|
||||||
.distinct()
|
|
||||||
)
|
|
||||||
return len(session.exec(statement).all()) > 0
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
class ProductServiceError(Exception):
|
|
||||||
def __init__(self, message: str):
|
|
||||||
super().__init__(message)
|
|
||||||
|
|
||||||
|
|
||||||
class ProductorNotFoundError(ProductServiceError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ProductNotFoundError(ProductServiceError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ProductCreateError(ProductServiceError):
|
|
||||||
def __init__(self, message: str, field: str | None = None):
|
|
||||||
super().__init__(message)
|
|
||||||
self.field = field
|
|
||||||
@@ -1,16 +1,13 @@
|
|||||||
|
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||||
import src.messages as messages
|
import src.messages as messages
|
||||||
import src.products.exceptions as exceptions
|
import src.models as models
|
||||||
import src.products.service as service
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.database import get_session
|
from src.database import get_session
|
||||||
|
from sqlmodel import Session
|
||||||
|
import src.products.service as service
|
||||||
|
from src.auth.auth import get_current_user
|
||||||
router = APIRouter(prefix='/products')
|
router = APIRouter(prefix='/products')
|
||||||
|
#user=Depends(get_current_user)
|
||||||
|
@router.get('/', response_model=list[models.ProductPublic], )
|
||||||
@router.get('', response_model=list[models.ProductPublic], )
|
|
||||||
def get_products(
|
def get_products(
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
@@ -20,13 +17,11 @@ def get_products(
|
|||||||
):
|
):
|
||||||
return service.get_all(
|
return service.get_all(
|
||||||
session,
|
session,
|
||||||
user,
|
|
||||||
names,
|
names,
|
||||||
productors,
|
productors,
|
||||||
types,
|
types,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get('/{id}', response_model=models.ProductPublic)
|
@router.get('/{id}', response_model=models.ProductPublic)
|
||||||
def get_product(
|
def get_product(
|
||||||
id: int,
|
id: int,
|
||||||
@@ -35,25 +30,16 @@ def get_product(
|
|||||||
):
|
):
|
||||||
result = service.get_one(session, id)
|
result = service.get_one(session, id)
|
||||||
if result is None:
|
if result is None:
|
||||||
raise HTTPException(status_code=404,
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
detail=messages.Messages.not_found('product'))
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@router.post('/', response_model=models.ProductPublic)
|
||||||
@router.post('', response_model=models.ProductPublic)
|
|
||||||
def create_product(
|
def create_product(
|
||||||
product: models.ProductCreate,
|
product: models.ProductCreate,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
return service.create_one(session, product)
|
||||||
result = service.create_one(session, product)
|
|
||||||
except exceptions.ProductCreateError as error:
|
|
||||||
raise HTTPException(status_code=400, detail=str(error))
|
|
||||||
except exceptions.ProductorNotFoundError as error:
|
|
||||||
raise HTTPException(status_code=404, detail=str(error))
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
@router.put('/{id}', response_model=models.ProductPublic)
|
@router.put('/{id}', response_model=models.ProductPublic)
|
||||||
def update_product(
|
def update_product(
|
||||||
@@ -61,23 +47,18 @@ def update_product(
|
|||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
result = service.update_one(session, id, product)
|
||||||
result = service.update_one(session, id, product)
|
if result is None:
|
||||||
except exceptions.ProductNotFoundError as error:
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
raise HTTPException(status_code=404, detail=str(error))
|
|
||||||
except exceptions.ProductorNotFoundError as error:
|
|
||||||
raise HTTPException(status_code=404, detail=str(error))
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@router.delete('/{id}', response_model=models.ProductPublic)
|
@router.delete('/{id}', response_model=models.ProductPublic)
|
||||||
def delete_product(
|
def delete_product(
|
||||||
id: int,
|
id: int,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
result = service.delete_one(session, id)
|
||||||
result = service.delete_one(session, id)
|
if result is None:
|
||||||
except exceptions.ProductNotFoundError as error:
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
raise HTTPException(status_code=404, detail=str(error))
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1,46 +1,25 @@
|
|||||||
import src.messages as messages
|
|
||||||
import src.products.exceptions as exceptions
|
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src import models
|
import src.models as models
|
||||||
|
|
||||||
|
|
||||||
def get_all(
|
def get_all(
|
||||||
session: Session,
|
session: Session,
|
||||||
user: models.User,
|
|
||||||
names: list[str],
|
names: list[str],
|
||||||
productors: list[str],
|
productors: list[str],
|
||||||
types: list[str],
|
types: list[str],
|
||||||
) -> list[models.ProductPublic]:
|
) -> list[models.ProductPublic]:
|
||||||
statement = select(
|
statement = select(models.Product)
|
||||||
models.Product) .join(
|
|
||||||
models.Productor,
|
|
||||||
models.Product.productor_id == models.Productor.id) .where(
|
|
||||||
models.Productor.type.in_(
|
|
||||||
[
|
|
||||||
r.name for r in user.roles])) .distinct()
|
|
||||||
if len(names) > 0:
|
if len(names) > 0:
|
||||||
statement = statement.where(models.Product.name.in_(names))
|
statement = statement.where(models.Product.name.in_(names))
|
||||||
if len(productors) > 0:
|
if len(productors) > 0:
|
||||||
statement = statement.where(models.Productor.name.in_(productors))
|
statement = statement.join(models.Productor).where(models.Productor.name.in_(productors))
|
||||||
if len(types) > 0:
|
if len(types) > 0:
|
||||||
statement = statement.where(models.Product.type.in_(types))
|
statement = statement.where(models.Product.type.in_(types))
|
||||||
return session.exec(statement.order_by(models.Product.name)).all()
|
return session.exec(statement.order_by(models.Product.name)).all()
|
||||||
|
|
||||||
|
|
||||||
def get_one(session: Session, product_id: int) -> models.ProductPublic:
|
def get_one(session: Session, product_id: int) -> models.ProductPublic:
|
||||||
return session.get(models.Product, product_id)
|
return session.get(models.Product, product_id)
|
||||||
|
|
||||||
|
def create_one(session: Session, product: models.ProductCreate) -> models.ProductPublic:
|
||||||
def create_one(
|
|
||||||
session: Session,
|
|
||||||
product: models.ProductCreate) -> models.ProductPublic:
|
|
||||||
if not product:
|
|
||||||
raise exceptions.ProductCreateError(
|
|
||||||
messages.Messages.invalid_input(
|
|
||||||
'product', 'input cannot be None'))
|
|
||||||
if not session.get(models.Productor, product.productor_id):
|
|
||||||
raise exceptions.ProductorNotFoundError(
|
|
||||||
messages.Messages.not_found('productor'))
|
|
||||||
product_create = product.model_dump(exclude_unset=True)
|
product_create = product.model_dump(exclude_unset=True)
|
||||||
new_product = models.Product(**product_create)
|
new_product = models.Product(**product_create)
|
||||||
session.add(new_product)
|
session.add(new_product)
|
||||||
@@ -48,22 +27,12 @@ def create_one(
|
|||||||
session.refresh(new_product)
|
session.refresh(new_product)
|
||||||
return new_product
|
return new_product
|
||||||
|
|
||||||
|
def update_one(session: Session, id: int, product: models.ProductUpdate) -> models.ProductPublic:
|
||||||
def update_one(
|
|
||||||
session: Session,
|
|
||||||
id: int,
|
|
||||||
product: models.ProductUpdate) -> models.ProductPublic:
|
|
||||||
statement = select(models.Product).where(models.Product.id == id)
|
statement = select(models.Product).where(models.Product.id == id)
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
new_product = result.first()
|
new_product = result.first()
|
||||||
if not new_product:
|
if not new_product:
|
||||||
raise exceptions.ProductNotFoundError(
|
return None
|
||||||
messages.Messages.not_found('product'))
|
|
||||||
if product.productor_id and not session.get(
|
|
||||||
models.Productor, product.productor_id):
|
|
||||||
raise exceptions.ProductorNotFoundError(
|
|
||||||
messages.Messages.not_found('productor'))
|
|
||||||
|
|
||||||
product_updates = product.model_dump(exclude_unset=True)
|
product_updates = product.model_dump(exclude_unset=True)
|
||||||
for key, value in product_updates.items():
|
for key, value in product_updates.items():
|
||||||
setattr(new_product, key, value)
|
setattr(new_product, key, value)
|
||||||
@@ -73,44 +42,13 @@ def update_one(
|
|||||||
session.refresh(new_product)
|
session.refresh(new_product)
|
||||||
return new_product
|
return new_product
|
||||||
|
|
||||||
|
|
||||||
def delete_one(session: Session, id: int) -> models.ProductPublic:
|
def delete_one(session: Session, id: int) -> models.ProductPublic:
|
||||||
statement = select(models.Product).where(models.Product.id == id)
|
statement = select(models.Product).where(models.Product.id == id)
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
product = result.first()
|
product = result.first()
|
||||||
if not product:
|
if not product:
|
||||||
raise exceptions.ProductNotFoundError(
|
return None
|
||||||
messages.Messages.not_found('product'))
|
|
||||||
result = models.ProductPublic.model_validate(product)
|
result = models.ProductPublic.model_validate(product)
|
||||||
session.delete(product)
|
session.delete(product)
|
||||||
session.commit()
|
session.commit()
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def is_allowed(
|
|
||||||
session: Session,
|
|
||||||
user: models.User,
|
|
||||||
_id: int,
|
|
||||||
product: models.ProductCreate
|
|
||||||
) -> bool:
|
|
||||||
if not _id:
|
|
||||||
statement = (
|
|
||||||
select(models.Product)
|
|
||||||
.join(
|
|
||||||
models.Productor,
|
|
||||||
models.Product.productor_id == models.Productor.id
|
|
||||||
)
|
|
||||||
.where(models.Product.id == product.productor_id)
|
|
||||||
)
|
|
||||||
productor = session.exec(statement).first()
|
|
||||||
return productor.type in [r.name for r in user.roles]
|
|
||||||
statement = (
|
|
||||||
select(models.Product)
|
|
||||||
.join(
|
|
||||||
models.Productor,
|
|
||||||
models.Product.productor_id == models.Productor.id
|
|
||||||
)
|
|
||||||
.where(models.Product.id == _id)
|
|
||||||
.where(models.Productor.type.in_([r.name for r in user.roles]))
|
|
||||||
.distinct()
|
|
||||||
)
|
|
||||||
return len(session.exec(statement).all()) > 0
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings
|
||||||
|
|
||||||
|
|
||||||
class Settings(BaseSettings):
|
class Settings(BaseSettings):
|
||||||
origins: str
|
origins: str
|
||||||
@@ -17,25 +16,13 @@ class Settings(BaseSettings):
|
|||||||
max_age: int
|
max_age: int
|
||||||
debug: bool
|
debug: bool
|
||||||
|
|
||||||
model_config = SettingsConfigDict(
|
class Config:
|
||||||
env_file='../.env'
|
env_file = "../.env"
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
|
||||||
AUTH_URL = (
|
AUTH_URL = f"{settings.keycloak_server}/realms/{settings.keycloak_realm}/protocol/openid-connect/auth"
|
||||||
f'{settings.keycloak_server}/realms/'
|
TOKEN_URL = f"{settings.keycloak_server}/realms/{settings.keycloak_realm}/protocol/openid-connect/token"
|
||||||
f'{settings.keycloak_realm}/protocol/openid-connect/auth'
|
ISSUER = f"{settings.keycloak_server}/realms/{settings.keycloak_realm}"
|
||||||
)
|
JWKS_URL = f"{ISSUER}/protocol/openid-connect/certs"
|
||||||
TOKEN_URL = (
|
LOGOUT_URL = f'{settings.keycloak_server}/realms/{settings.keycloak_realm}/protocol/openid-connect/logout'
|
||||||
f'{settings.keycloak_server}/realms/'
|
|
||||||
f'{settings.keycloak_realm}/protocol/openid-connect/token'
|
|
||||||
)
|
|
||||||
|
|
||||||
ISSUER = f'{settings.keycloak_server}/realms/{settings.keycloak_realm}'
|
|
||||||
JWKS_URL = f'{ISSUER}/protocol/openid-connect/certs'
|
|
||||||
LOGOUT_URL = (
|
|
||||||
f'{settings.keycloak_server}/realms/'
|
|
||||||
f'{settings.keycloak_realm}/protocol/openid-connect/logout'
|
|
||||||
)
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
|
|
||||||
class ShipmentServiceError(Exception):
|
|
||||||
def __init__(self, message: str):
|
|
||||||
super().__init__(message)
|
|
||||||
logging.error('ShipmentService : %s', message)
|
|
||||||
|
|
||||||
|
|
||||||
class ShipmentNotFoundError(ShipmentServiceError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ShipmentCreateError(ShipmentServiceError):
|
|
||||||
def __init__(self, message: str, field: str | None = None):
|
|
||||||
super().__init__(message)
|
|
||||||
self.field = field
|
|
||||||
@@ -1,111 +1,46 @@
|
|||||||
# pylint: disable=E1101
|
|
||||||
import datetime
|
|
||||||
|
|
||||||
import src.messages as messages
|
|
||||||
import src.shipments.exceptions as exceptions
|
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src import models
|
import src.models as models
|
||||||
|
|
||||||
|
|
||||||
def get_all(
|
def get_all(
|
||||||
session: Session,
|
session: Session,
|
||||||
user: models.User,
|
names: list[str],
|
||||||
names: list[str] = None,
|
dates: list[str],
|
||||||
dates: list[str] = None,
|
forms: list[int]
|
||||||
forms: list[str] = None
|
|
||||||
) -> list[models.ShipmentPublic]:
|
) -> list[models.ShipmentPublic]:
|
||||||
statement = (
|
statement = select(models.Shipment)
|
||||||
select(models.Shipment)
|
if len(names) > 0:
|
||||||
.join(
|
|
||||||
models.Form,
|
|
||||||
models.Shipment.form_id == models.Form.id)
|
|
||||||
.join(
|
|
||||||
models.Productor,
|
|
||||||
models.Form.productor_id == models.Productor.id)
|
|
||||||
.where(
|
|
||||||
models.Productor.type.in_(
|
|
||||||
[r.name for r in user.roles]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.distinct()
|
|
||||||
)
|
|
||||||
if names and len(names) > 0:
|
|
||||||
statement = statement.where(models.Shipment.name.in_(names))
|
statement = statement.where(models.Shipment.name.in_(names))
|
||||||
if dates and len(dates) > 0:
|
if len(dates) > 0:
|
||||||
statement = statement.where(
|
statement = statement.where(models.Shipment.date.in_(list(map(lambda x: datetime.strptime(x, '%Y-%m-%d'), dates))))
|
||||||
models.Shipment.date.in_(
|
if len(forms) > 0:
|
||||||
list(map(
|
statement = statement.join(models.Form).where(models.Form.name.in_(forms))
|
||||||
lambda x: datetime.datetime.strptime(
|
|
||||||
x, '%Y-%m-%d').date(),
|
|
||||||
dates
|
|
||||||
))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if forms and len(forms) > 0:
|
|
||||||
statement = statement.where(models.Form.name.in_(forms))
|
|
||||||
return session.exec(statement.order_by(models.Shipment.name)).all()
|
return session.exec(statement.order_by(models.Shipment.name)).all()
|
||||||
|
|
||||||
|
|
||||||
def get_one(session: Session, shipment_id: int) -> models.ShipmentPublic:
|
def get_one(session: Session, shipment_id: int) -> models.ShipmentPublic:
|
||||||
return session.get(models.Shipment, shipment_id)
|
return session.get(models.Shipment, shipment_id)
|
||||||
|
|
||||||
|
def create_one(session: Session, shipment: models.ShipmentCreate) -> models.ShipmentPublic:
|
||||||
def create_one(
|
products = session.exec(select(models.Product).where(models.Product.id.in_(shipment.product_ids))).all()
|
||||||
session: Session,
|
shipment_create = shipment.model_dump(exclude_unset=True, exclude={'product_ids'})
|
||||||
shipment: models.ShipmentCreate) -> models.ShipmentPublic:
|
|
||||||
if shipment is None:
|
|
||||||
raise exceptions.ShipmentCreateError(
|
|
||||||
messages.Messages.invalid_input(
|
|
||||||
'shipment', 'input cannot be None'))
|
|
||||||
products = session.exec(
|
|
||||||
select(models.Product)
|
|
||||||
.where(
|
|
||||||
models.Product.id.in_(
|
|
||||||
shipment.product_ids
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).all()
|
|
||||||
shipment_create = shipment.model_dump(
|
|
||||||
exclude_unset=True, exclude={'product_ids'}
|
|
||||||
)
|
|
||||||
new_shipment = models.Shipment(**shipment_create, products=products)
|
new_shipment = models.Shipment(**shipment_create, products=products)
|
||||||
session.add(new_shipment)
|
session.add(new_shipment)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(new_shipment)
|
session.refresh(new_shipment)
|
||||||
return new_shipment
|
return new_shipment
|
||||||
|
|
||||||
|
def update_one(session: Session, id: int, shipment: models.ShipmentUpdate) -> models.ShipmentPublic:
|
||||||
def update_one(
|
statement = select(models.Shipment).where(models.Shipment.id == id)
|
||||||
session: Session,
|
|
||||||
_id: int,
|
|
||||||
shipment: models.ShipmentUpdate) -> models.ShipmentPublic:
|
|
||||||
if shipment is None:
|
|
||||||
raise exceptions.ShipmentCreateError(
|
|
||||||
messages.Messages.invalid_input(
|
|
||||||
'shipment', 'input cannot be None'))
|
|
||||||
statement = select(models.Shipment).where(models.Shipment.id == _id)
|
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
new_shipment = result.first()
|
new_shipment = result.first()
|
||||||
if not new_shipment:
|
if not new_shipment:
|
||||||
raise exceptions.ShipmentNotFoundError(
|
return None
|
||||||
messages.Messages.not_found('shipment'))
|
|
||||||
|
|
||||||
products_to_add = session.exec(
|
products_to_add = session.exec(select(models.Product).where(models.Product.id.in_(shipment.product_ids))).all()
|
||||||
select(
|
|
||||||
models.Product
|
|
||||||
).where(
|
|
||||||
models.Product.id.in_(
|
|
||||||
shipment.product_ids
|
|
||||||
)
|
|
||||||
)
|
|
||||||
).all()
|
|
||||||
new_shipment.products.clear()
|
new_shipment.products.clear()
|
||||||
for add in products_to_add:
|
for add in products_to_add:
|
||||||
new_shipment.products.append(add)
|
new_shipment.products.append(add)
|
||||||
|
|
||||||
shipment_updates = shipment.model_dump(
|
shipment_updates = shipment.model_dump(exclude_unset=True, exclude={"product_ids"})
|
||||||
exclude_unset=True, exclude={"product_ids"}
|
|
||||||
)
|
|
||||||
for key, value in shipment_updates.items():
|
for key, value in shipment_updates.items():
|
||||||
setattr(new_shipment, key, value)
|
setattr(new_shipment, key, value)
|
||||||
|
|
||||||
@@ -114,15 +49,12 @@ def update_one(
|
|||||||
session.refresh(new_shipment)
|
session.refresh(new_shipment)
|
||||||
return new_shipment
|
return new_shipment
|
||||||
|
|
||||||
|
def delete_one(session: Session, id: int) -> models.ShipmentPublic:
|
||||||
def delete_one(session: Session, _id: int) -> models.ShipmentPublic:
|
statement = select(models.Shipment).where(models.Shipment.id == id)
|
||||||
statement = select(models.Shipment).where(models.Shipment.id == _id)
|
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
shipment = result.first()
|
shipment = result.first()
|
||||||
if not shipment:
|
if not shipment:
|
||||||
raise exceptions.ShipmentNotFoundError(
|
return None
|
||||||
messages.Messages.not_found('shipment'))
|
|
||||||
|
|
||||||
result = models.ShipmentPublic.model_validate(shipment)
|
result = models.ShipmentPublic.model_validate(shipment)
|
||||||
session.delete(shipment)
|
session.delete(shipment)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|||||||
@@ -1,82 +1,64 @@
|
|||||||
|
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||||
import src.messages as messages
|
import src.messages as messages
|
||||||
import src.shipments.exceptions as exceptions
|
import src.models as models
|
||||||
import src.shipments.service as service
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.database import get_session
|
from src.database import get_session
|
||||||
|
from sqlmodel import Session
|
||||||
|
import src.shipments.service as service
|
||||||
|
from src.auth.auth import get_current_user
|
||||||
|
|
||||||
router = APIRouter(prefix='/shipments')
|
router = APIRouter(prefix='/shipments')
|
||||||
|
|
||||||
|
@router.get('/', response_model=list[models.ShipmentPublic], )
|
||||||
@router.get('', response_model=list[models.ShipmentPublic], )
|
|
||||||
def get_shipments(
|
def get_shipments(
|
||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
user: models.User = Depends(get_current_user),
|
|
||||||
names: list[str] = Query([]),
|
names: list[str] = Query([]),
|
||||||
dates: list[str] = Query([]),
|
dates: list[str] = Query([]),
|
||||||
forms: list[str] = Query([]),
|
forms: list[str] = Query([]),
|
||||||
):
|
):
|
||||||
return service.get_all(
|
return service.get_all(
|
||||||
session,
|
session,
|
||||||
user,
|
|
||||||
names,
|
names,
|
||||||
dates,
|
dates,
|
||||||
forms,
|
forms,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@router.get('/{id}', response_model=models.ShipmentPublic)
|
||||||
@router.get('/{_id}', response_model=models.ShipmentPublic)
|
|
||||||
def get_shipment(
|
def get_shipment(
|
||||||
_id: int,
|
id: int,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
result = service.get_one(session, _id)
|
result = service.get_one(session, id)
|
||||||
if result is None:
|
if result is None:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
status_code=404,
|
|
||||||
detail=messages.Messages.not_found('shipment')
|
|
||||||
)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@router.post('/', response_model=models.ShipmentPublic)
|
||||||
@router.post('', response_model=models.ShipmentPublic)
|
|
||||||
def create_shipment(
|
def create_shipment(
|
||||||
shipment: models.ShipmentCreate,
|
shipment: models.ShipmentCreate,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
return service.create_one(session, shipment)
|
||||||
result = service.create_one(session, shipment)
|
|
||||||
except exceptions.ShipmentCreateError as error:
|
|
||||||
raise HTTPException(status_code=400, detail=str(error)) from error
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
@router.put('/{id}', response_model=models.ShipmentPublic)
|
||||||
@router.put('/{_id}', response_model=models.ShipmentPublic)
|
|
||||||
def update_shipment(
|
def update_shipment(
|
||||||
_id: int,
|
id: int, shipment: models.ShipmentUpdate,
|
||||||
shipment: models.ShipmentUpdate,
|
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
result = service.update_one(session, id, shipment)
|
||||||
result = service.update_one(session, _id, shipment)
|
if result is None:
|
||||||
except exceptions.ShipmentNotFoundError as error:
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@router.delete('/{id}', response_model=models.ShipmentPublic)
|
||||||
@router.delete('/{_id}', response_model=models.ShipmentPublic)
|
|
||||||
def delete_shipment(
|
def delete_shipment(
|
||||||
_id: int,
|
id: int,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
result = service.delete_one(session, id)
|
||||||
result = service.delete_one(session, _id)
|
if result is None:
|
||||||
except exceptions.ShipmentNotFoundError as error:
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
raise HTTPException(status_code=404, detail=str(error)) from error
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src import models
|
import src.models as models
|
||||||
|
|
||||||
|
|
||||||
def get_all(session: Session) -> list[models.TemplatePublic]:
|
def get_all(session: Session) -> list[models.TemplatePublic]:
|
||||||
statement = select(models.Template)
|
statement = select(models.Template)
|
||||||
return session.exec(statement.order_by(models.Template.name)).all()
|
return session.exec(statement.order_by(models.Template.name)).all()
|
||||||
|
|
||||||
|
|
||||||
def get_one(session: Session, template_id: int) -> models.TemplatePublic:
|
def get_one(session: Session, template_id: int) -> models.TemplatePublic:
|
||||||
return session.get(models.Template, template_id)
|
return session.get(models.Template, template_id)
|
||||||
|
|
||||||
|
def create_one(session: Session, template: models.TemplateCreate) -> models.TemplatePublic:
|
||||||
def create_one(
|
|
||||||
session: Session,
|
|
||||||
template: models.TemplateCreate) -> models.TemplatePublic:
|
|
||||||
template_create = template.model_dump(exclude_unset=True)
|
template_create = template.model_dump(exclude_unset=True)
|
||||||
new_template = models.Template(**template_create)
|
new_template = models.Template(**template_create)
|
||||||
session.add(new_template)
|
session.add(new_template)
|
||||||
@@ -21,11 +16,7 @@ def create_one(
|
|||||||
session.refresh(new_template)
|
session.refresh(new_template)
|
||||||
return new_template
|
return new_template
|
||||||
|
|
||||||
|
def update_one(session: Session, id: int, template: models.TemplateUpdate) -> models.TemplatePublic:
|
||||||
def update_one(
|
|
||||||
session: Session,
|
|
||||||
id: int,
|
|
||||||
template: models.TemplateUpdate) -> models.TemplatePublic:
|
|
||||||
statement = select(models.Template).where(models.Template.id == id)
|
statement = select(models.Template).where(models.Template.id == id)
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
new_template = result.first()
|
new_template = result.first()
|
||||||
@@ -39,7 +30,6 @@ def update_one(
|
|||||||
session.refresh(new_template)
|
session.refresh(new_template)
|
||||||
return new_template
|
return new_template
|
||||||
|
|
||||||
|
|
||||||
def delete_one(session: Session, id: int) -> models.TemplatePublic:
|
def delete_one(session: Session, id: int) -> models.TemplatePublic:
|
||||||
statement = select(models.Template).where(models.Template.id == id)
|
statement = select(models.Template).where(models.Template.id == id)
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
|
|||||||
@@ -1,22 +1,20 @@
|
|||||||
|
from fastapi import APIRouter, HTTPException, Depends
|
||||||
import src.messages as messages
|
import src.messages as messages
|
||||||
import src.templates.service as service
|
import src.models as models
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.database import get_session
|
from src.database import get_session
|
||||||
|
from sqlmodel import Session
|
||||||
|
import src.templates.service as service
|
||||||
|
from src.auth.auth import get_current_user
|
||||||
|
|
||||||
router = APIRouter(prefix='/templates')
|
router = APIRouter(prefix='/templates')
|
||||||
|
|
||||||
|
@router.get('/', response_model=list[models.TemplatePublic])
|
||||||
@router.get('', response_model=list[models.TemplatePublic])
|
|
||||||
def get_templates(
|
def get_templates(
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
return service.get_all(session)
|
return service.get_all(session)
|
||||||
|
|
||||||
|
|
||||||
@router.get('/{id}', response_model=models.TemplatePublic)
|
@router.get('/{id}', response_model=models.TemplatePublic)
|
||||||
def get_template(
|
def get_template(
|
||||||
id: int,
|
id: int,
|
||||||
@@ -25,12 +23,10 @@ def get_template(
|
|||||||
):
|
):
|
||||||
result = service.get_one(session, id)
|
result = service.get_one(session, id)
|
||||||
if result is None:
|
if result is None:
|
||||||
raise HTTPException(status_code=404,
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
detail=messages.Messages.not_found('template'))
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@router.post('/', response_model=models.TemplatePublic)
|
||||||
@router.post('', response_model=models.TemplatePublic)
|
|
||||||
def create_template(
|
def create_template(
|
||||||
template: models.TemplateCreate,
|
template: models.TemplateCreate,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
@@ -38,7 +34,6 @@ def create_template(
|
|||||||
):
|
):
|
||||||
return service.create_one(session, template)
|
return service.create_one(session, template)
|
||||||
|
|
||||||
|
|
||||||
@router.put('/{id}', response_model=models.TemplatePublic)
|
@router.put('/{id}', response_model=models.TemplatePublic)
|
||||||
def update_template(
|
def update_template(
|
||||||
id: int, template: models.TemplateUpdate,
|
id: int, template: models.TemplateUpdate,
|
||||||
@@ -47,11 +42,9 @@ def update_template(
|
|||||||
):
|
):
|
||||||
result = service.update_one(session, id, template)
|
result = service.update_one(session, id, template)
|
||||||
if result is None:
|
if result is None:
|
||||||
raise HTTPException(status_code=404,
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
detail=messages.Messages.not_found('template'))
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@router.delete('/{id}', response_model=models.TemplatePublic)
|
@router.delete('/{id}', response_model=models.TemplatePublic)
|
||||||
def delete_template(
|
def delete_template(
|
||||||
id: int,
|
id: int,
|
||||||
@@ -60,6 +53,5 @@ def delete_template(
|
|||||||
):
|
):
|
||||||
result = service.delete_one(session, id)
|
result = service.delete_one(session, id)
|
||||||
if result is None:
|
if result is None:
|
||||||
raise HTTPException(status_code=404,
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
detail=messages.Messages.not_found('template'))
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import logging
|
|
||||||
|
|
||||||
|
|
||||||
class UserServiceError(Exception):
|
|
||||||
def __init__(self, message: str):
|
|
||||||
super().__init__(message)
|
|
||||||
logging.error('UserService : %s', message)
|
|
||||||
|
|
||||||
|
|
||||||
class UserNotFoundError(UserServiceError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class UserCreateError(UserServiceError):
|
|
||||||
def __init__(self, message: str, field: str | None = None):
|
|
||||||
super().__init__(message)
|
|
||||||
self.field = field
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
import src.messages as messages
|
|
||||||
import src.users.exceptions as exceptions
|
|
||||||
from sqlmodel import Session, select
|
from sqlmodel import Session, select
|
||||||
from src import models
|
import src.models as models
|
||||||
|
|
||||||
|
|
||||||
def get_all(
|
def get_all(
|
||||||
session: Session,
|
session: Session,
|
||||||
@@ -16,15 +13,11 @@ def get_all(
|
|||||||
statement = statement.where(models.User.email.in_(emails))
|
statement = statement.where(models.User.email.in_(emails))
|
||||||
return session.exec(statement.order_by(models.User.name)).all()
|
return session.exec(statement.order_by(models.User.name)).all()
|
||||||
|
|
||||||
|
|
||||||
def get_one(session: Session, user_id: int) -> models.UserPublic:
|
def get_one(session: Session, user_id: int) -> models.UserPublic:
|
||||||
return session.get(models.User, user_id)
|
return session.get(models.User, user_id)
|
||||||
|
|
||||||
|
def get_or_create_roles(session: Session, role_names: list[str]) -> list[models.ContractType]:
|
||||||
def get_or_create_roles(session: Session,
|
statement = select(models.ContractType).where(models.ContractType.name.in_(role_names))
|
||||||
role_names: list[str]) -> list[models.ContractType]:
|
|
||||||
statement = select(models.ContractType).where(
|
|
||||||
models.ContractType.name.in_(role_names))
|
|
||||||
existing = session.exec(statement).all()
|
existing = session.exec(statement).all()
|
||||||
existing_roles = {role.name for role in existing}
|
existing_roles = {role.name for role in existing}
|
||||||
missing_role = set(role_names) - existing_roles
|
missing_role = set(role_names) - existing_roles
|
||||||
@@ -40,11 +33,8 @@ def get_or_create_roles(session: Session,
|
|||||||
session.refresh(role)
|
session.refresh(role)
|
||||||
return existing + new_roles
|
return existing + new_roles
|
||||||
|
|
||||||
|
|
||||||
def get_or_create_user(session: Session, user_create: models.UserCreate):
|
def get_or_create_user(session: Session, user_create: models.UserCreate):
|
||||||
statement = select(
|
statement = select(models.User).where(models.User.email == user_create.email)
|
||||||
models.User).where(
|
|
||||||
models.User.email == user_create.email)
|
|
||||||
user = session.exec(statement).first()
|
user = session.exec(statement).first()
|
||||||
if user:
|
if user:
|
||||||
user_role_names = [r.name for r in user.roles]
|
user_role_names = [r.name for r in user.roles]
|
||||||
@@ -54,21 +44,11 @@ def get_or_create_user(session: Session, user_create: models.UserCreate):
|
|||||||
user = create_one(session, user_create)
|
user = create_one(session, user_create)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
def get_roles(session: Session):
|
def get_roles(session: Session):
|
||||||
statement = (
|
statement = select(models.ContractType)
|
||||||
select(models.ContractType)
|
|
||||||
)
|
|
||||||
return session.exec(statement.order_by(models.ContractType.name)).all()
|
return session.exec(statement.order_by(models.ContractType.name)).all()
|
||||||
|
|
||||||
|
|
||||||
def create_one(session: Session, user: models.UserCreate) -> models.UserPublic:
|
def create_one(session: Session, user: models.UserCreate) -> models.UserPublic:
|
||||||
if user is None:
|
|
||||||
raise exceptions.UserCreateError(
|
|
||||||
messages.Messages.invalid_input(
|
|
||||||
'user', 'input cannot be None'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
new_user = models.User(
|
new_user = models.User(
|
||||||
name=user.name,
|
name=user.name,
|
||||||
email=user.email
|
email=user.email
|
||||||
@@ -82,39 +62,31 @@ def create_one(session: Session, user: models.UserCreate) -> models.UserPublic:
|
|||||||
session.refresh(new_user)
|
session.refresh(new_user)
|
||||||
return new_user
|
return new_user
|
||||||
|
|
||||||
|
def update_one(session: Session, id: int, user: models.UserCreate) -> models.UserPublic:
|
||||||
def update_one(
|
statement = select(models.User).where(models.User.id == id)
|
||||||
session: Session,
|
|
||||||
_id: int,
|
|
||||||
user: models.UserCreate) -> models.UserPublic:
|
|
||||||
if user is None:
|
|
||||||
raise exceptions.UserCreateError(
|
|
||||||
messages.Messages.invalid_input(
|
|
||||||
'user', 'input cannot be None'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
statement = select(models.User).where(models.User.id == _id)
|
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
new_user = result.first()
|
new_user = result.first()
|
||||||
if not new_user:
|
if not new_user:
|
||||||
raise exceptions.UserNotFoundError(f'User {_id} not found')
|
return None
|
||||||
new_user.email = user.email
|
|
||||||
new_user.name = user.name
|
user_updates = user.model_dump(exclude="role_names")
|
||||||
|
for key, value in user_updates.items():
|
||||||
|
setattr(new_user, key, value)
|
||||||
|
|
||||||
roles = get_or_create_roles(session, user.role_names)
|
roles = get_or_create_roles(session, user.role_names)
|
||||||
new_user.roles = roles
|
new_user.roles = roles
|
||||||
|
|
||||||
session.add(new_user)
|
session.add(new_user)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(new_user)
|
session.refresh(new_user)
|
||||||
return new_user
|
return new_user
|
||||||
|
|
||||||
|
def delete_one(session: Session, id: int) -> models.UserPublic:
|
||||||
def delete_one(session: Session, _id: int) -> models.UserPublic:
|
statement = select(models.User).where(models.User.id == id)
|
||||||
statement = select(models.User).where(models.User.id == _id)
|
|
||||||
result = session.exec(statement)
|
result = session.exec(statement)
|
||||||
user = result.first()
|
user = result.first()
|
||||||
if not user:
|
if not user:
|
||||||
raise exceptions.UserNotFoundError(f'User {_id} not found')
|
return None
|
||||||
result = models.UserPublic.model_validate(user)
|
result = models.UserPublic.model_validate(user)
|
||||||
session.delete(user)
|
session.delete(user)
|
||||||
session.commit()
|
session.commit()
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
|
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||||
import src.messages as messages
|
import src.messages as messages
|
||||||
import src.users.exceptions as exceptions
|
import src.models as models
|
||||||
import src.users.service as service
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.database import get_session
|
from src.database import get_session
|
||||||
|
from sqlmodel import Session
|
||||||
|
import src.users.service as service
|
||||||
|
from src.auth.auth import get_current_user
|
||||||
|
|
||||||
router = APIRouter(prefix='/users')
|
router = APIRouter(prefix='/users')
|
||||||
|
|
||||||
|
@router.get('/', response_model=list[models.UserPublic])
|
||||||
@router.get('', response_model=list[models.UserPublic])
|
|
||||||
def get_users(
|
def get_users(
|
||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
@@ -23,7 +21,6 @@ def get_users(
|
|||||||
emails,
|
emails,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get('/roles', response_model=list[models.ContractType])
|
@router.get('/roles', response_model=list[models.ContractType])
|
||||||
def get_roles(
|
def get_roles(
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
@@ -31,66 +28,44 @@ def get_roles(
|
|||||||
):
|
):
|
||||||
return service.get_roles(session)
|
return service.get_roles(session)
|
||||||
|
|
||||||
|
@router.get('/{id}', response_model=models.UserPublic)
|
||||||
@router.get('/{_id}', response_model=models.UserPublic)
|
def get_users(
|
||||||
def get_user(
|
id: int,
|
||||||
_id: int,
|
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
result = service.get_one(session, _id)
|
result = service.get_one(session, id)
|
||||||
if result is None:
|
if result is None:
|
||||||
raise HTTPException(
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
status_code=404,
|
|
||||||
detail=messages.Messages.not_found('user')
|
|
||||||
)
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@router.post('/', response_model=models.UserPublic)
|
||||||
@router.post('', response_model=models.UserPublic)
|
|
||||||
def create_user(
|
def create_user(
|
||||||
user: models.UserCreate,
|
user: models.UserCreate,
|
||||||
logged_user: models.User = Depends(get_current_user),
|
logged_user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
return service.create_one(session, user)
|
||||||
user = service.create_one(session, user)
|
|
||||||
except exceptions.UserCreateError as error:
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400,
|
|
||||||
detail=str(error)
|
|
||||||
) from error
|
|
||||||
return user
|
|
||||||
|
|
||||||
|
@router.put('/{id}', response_model=models.UserPublic)
|
||||||
@router.put('/{_id}', response_model=models.UserPublic)
|
|
||||||
def update_user(
|
def update_user(
|
||||||
_id: int,
|
id: int,
|
||||||
user: models.UserUpdate,
|
user: models.UserUpdate,
|
||||||
logged_user: models.User = Depends(get_current_user),
|
logged_user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
result = service.update_one(session, id, user)
|
||||||
result = service.update_one(session, _id, user)
|
if result is None:
|
||||||
except exceptions.UserNotFoundError as error:
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
raise HTTPException(
|
|
||||||
status_code=404,
|
|
||||||
detail=messages.Messages.not_found('user')
|
|
||||||
) from error
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@router.delete('/{id}', response_model=models.UserPublic)
|
@router.delete('/{id}', response_model=models.UserPublic)
|
||||||
def delete_user(
|
def delete_user(
|
||||||
id: int,
|
id: int,
|
||||||
user: models.User = Depends(get_current_user),
|
user: models.User = Depends(get_current_user),
|
||||||
session: Session = Depends(get_session)
|
session: Session = Depends(get_session)
|
||||||
):
|
):
|
||||||
try:
|
result = service.delete_one(session, id)
|
||||||
result = service.delete_one(session, id)
|
if result is None:
|
||||||
except exceptions.UserNotFoundError as error:
|
raise HTTPException(status_code=404, detail=messages.notfound)
|
||||||
raise HTTPException(
|
|
||||||
status_code=404,
|
|
||||||
detail=messages.Messages.not_found('user')
|
|
||||||
) from error
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
import pytest
|
|
||||||
from fastapi.testclient import TestClient
|
|
||||||
from sqlalchemy.pool import StaticPool
|
|
||||||
from sqlmodel import Session, SQLModel, create_engine
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.database import get_session
|
|
||||||
from src.main import app
|
|
||||||
|
|
||||||
from .fixtures import *
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_session(mocker):
|
|
||||||
session = mocker.Mock()
|
|
||||||
|
|
||||||
def override():
|
|
||||||
return session
|
|
||||||
|
|
||||||
app.dependency_overrides[get_session] = override
|
|
||||||
yield session
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def mock_user():
|
|
||||||
user = models.User(id=1, name='test user', email='test@user.com')
|
|
||||||
|
|
||||||
def override():
|
|
||||||
return user
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = override
|
|
||||||
yield user
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def client():
|
|
||||||
return TestClient(app)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name='session')
|
|
||||||
def session_fixture():
|
|
||||||
engine = create_engine(
|
|
||||||
"sqlite://",
|
|
||||||
connect_args={"check_same_thread": False},
|
|
||||||
poolclass=StaticPool,
|
|
||||||
)
|
|
||||||
|
|
||||||
SQLModel.metadata.create_all(engine)
|
|
||||||
|
|
||||||
connection = engine.connect()
|
|
||||||
transaction = connection.begin()
|
|
||||||
session = Session(bind=connection)
|
|
||||||
|
|
||||||
try:
|
|
||||||
yield session
|
|
||||||
finally:
|
|
||||||
transaction.rollback()
|
|
||||||
session.close()
|
|
||||||
connection.close()
|
|
||||||
engine.dispose()
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import tests.factories.contracts as contract_factory
|
|
||||||
import tests.factories.products as product_factory
|
|
||||||
from src import models
|
|
||||||
|
|
||||||
|
|
||||||
def contract_product_factory(**kwargs):
|
|
||||||
contract = contract_factory.contract_factory(id=1)
|
|
||||||
product = product_factory.product_public_factory(
|
|
||||||
id=1, type=models.ProductType.RECCURENT)
|
|
||||||
data = dict(
|
|
||||||
product_id=1,
|
|
||||||
shipment_id=1,
|
|
||||||
quantity=1,
|
|
||||||
contract_id=1,
|
|
||||||
product=product,
|
|
||||||
contract=contract
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ContractProduct(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def contract_product_public_factory(**kwargs):
|
|
||||||
contract = contract_factory.contract_factory(id=1)
|
|
||||||
product = product_factory.product_public_factory(id=1)
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
product_id=1,
|
|
||||||
shipment_id=None,
|
|
||||||
contract=contract,
|
|
||||||
product=product,
|
|
||||||
shipment=None,
|
|
||||||
quantity=1
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ContractProductPublic(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def contract_product_create_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
product_id=1,
|
|
||||||
shipment_id=1,
|
|
||||||
quantity=1,
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ContractProductCreate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def contract_product_update_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
product_id=1,
|
|
||||||
shipment_id=1,
|
|
||||||
quantity=1,
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ContractProductUpdate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def contract_product_body_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
product_id=1,
|
|
||||||
shipment_id=1,
|
|
||||||
quantity=1,
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return data
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
from src import models
|
|
||||||
|
|
||||||
from .forms import form_factory
|
|
||||||
|
|
||||||
|
|
||||||
def contract_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
firstname="test",
|
|
||||||
lastname="test",
|
|
||||||
email="test@test.test",
|
|
||||||
phone="00000000",
|
|
||||||
payment_method="cheque",
|
|
||||||
cheque_quantity=1,
|
|
||||||
form_id=1,
|
|
||||||
products=[],
|
|
||||||
cheques=[],
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.Contract(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def contract_public_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
firstname="test",
|
|
||||||
lastname="test",
|
|
||||||
email="test@test.test",
|
|
||||||
phone="00000000",
|
|
||||||
payment_method="cheque",
|
|
||||||
cheque_quantity=1,
|
|
||||||
total_price=10,
|
|
||||||
products=[],
|
|
||||||
form=form_factory()
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ContractPublic(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def contract_create_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
firstname="test",
|
|
||||||
lastname="test",
|
|
||||||
email="test@test.test",
|
|
||||||
phone="00000000",
|
|
||||||
payment_method="cheque",
|
|
||||||
cheque_quantity=1,
|
|
||||||
products=[],
|
|
||||||
cheques=[],
|
|
||||||
form_id=1,
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ContractCreate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def contract_update_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
firstname="test",
|
|
||||||
lastname="test",
|
|
||||||
email="test@test.test",
|
|
||||||
phone="00000000",
|
|
||||||
payment_method="cheque",
|
|
||||||
cheque_quantity=1,
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ContractUpdate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def contract_body_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
firstname="test",
|
|
||||||
lastname="test",
|
|
||||||
email="test@test.test",
|
|
||||||
phone="00000000",
|
|
||||||
payment_method="cheque",
|
|
||||||
cheque_quantity=1,
|
|
||||||
products=[],
|
|
||||||
cheques=[],
|
|
||||||
form_id=1
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return data
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
import datetime
|
|
||||||
|
|
||||||
from src import models
|
|
||||||
|
|
||||||
from .productors import productor_public_factory
|
|
||||||
from .users import user_factory
|
|
||||||
|
|
||||||
|
|
||||||
def form_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name='form 1',
|
|
||||||
productor_id=1,
|
|
||||||
referer_id=1,
|
|
||||||
season='hiver-2026',
|
|
||||||
start=datetime.date(2025, 10, 10),
|
|
||||||
end=datetime.date(2025, 10, 10),
|
|
||||||
minimum_shipment_value=0,
|
|
||||||
visible=True,
|
|
||||||
referer=user_factory(),
|
|
||||||
shipments=[],
|
|
||||||
productor=productor_public_factory(),
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.Form(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def form_body_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name='form 1',
|
|
||||||
productor_id=1,
|
|
||||||
referer_id=1,
|
|
||||||
season='hiver-2026',
|
|
||||||
start='2025-10-10',
|
|
||||||
end='2025-10-10',
|
|
||||||
minimum_shipment_value=0,
|
|
||||||
visible=True
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def form_create_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name='form 1',
|
|
||||||
productor_id=1,
|
|
||||||
referer_id=1,
|
|
||||||
season='hiver-2026',
|
|
||||||
start=datetime.date(2025, 10, 10),
|
|
||||||
end=datetime.date(2025, 10, 10),
|
|
||||||
minimum_shipment_value=0,
|
|
||||||
visible=True
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.FormCreate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def form_update_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name='form 1',
|
|
||||||
productor_id=1,
|
|
||||||
referer_id=1,
|
|
||||||
season='hiver-2026',
|
|
||||||
start=datetime.date(2025, 10, 10),
|
|
||||||
end=datetime.date(2025, 10, 10),
|
|
||||||
minimum_shipment_value=0,
|
|
||||||
visible=True
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.FormUpdate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def form_public_factory(form=None, shipments=[], **kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name='form 1',
|
|
||||||
productor_id=1,
|
|
||||||
referer_id=1,
|
|
||||||
season='hiver-2026',
|
|
||||||
start=datetime.date(2025, 10, 10),
|
|
||||||
end=datetime.date(2025, 10, 10),
|
|
||||||
minimum_shipment_value=0,
|
|
||||||
visible=True,
|
|
||||||
referer=user_factory(),
|
|
||||||
shipments=[],
|
|
||||||
productor=productor_public_factory(),
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.FormPublic(**data)
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
from src import models
|
|
||||||
|
|
||||||
|
|
||||||
def productor_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name="test productor",
|
|
||||||
address="test address",
|
|
||||||
type="test type"
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.Productor(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def productor_public_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name="test productor",
|
|
||||||
address="test address",
|
|
||||||
type="test type",
|
|
||||||
products=[],
|
|
||||||
payment_methods=[],
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ProductorPublic(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def productor_create_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name="test productor",
|
|
||||||
address="test address",
|
|
||||||
type="test type",
|
|
||||||
products=[],
|
|
||||||
payment_methods=[],
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ProductorCreate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def productor_update_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name="test productor",
|
|
||||||
address="test address",
|
|
||||||
type="test type",
|
|
||||||
products=[],
|
|
||||||
payment_methods=[],
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ProductorUpdate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def productor_body_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name="test productor",
|
|
||||||
address="test address",
|
|
||||||
type="test type",
|
|
||||||
products=[],
|
|
||||||
payment_methods=[],
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return data
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
from src import models
|
|
||||||
|
|
||||||
from .productors import productor_factory
|
|
||||||
|
|
||||||
|
|
||||||
def product_body_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name='product test 1',
|
|
||||||
unit=models.Unit.PIECE,
|
|
||||||
price=10.2,
|
|
||||||
price_kg=20.4,
|
|
||||||
quantity=500,
|
|
||||||
quantity_unit='g',
|
|
||||||
type=models.ProductType.OCCASIONAL,
|
|
||||||
productor_id=1,
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def product_create_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name='product test 1',
|
|
||||||
unit=models.Unit.PIECE,
|
|
||||||
price=10.2,
|
|
||||||
price_kg=20.4,
|
|
||||||
quantity=500,
|
|
||||||
quantity_unit='g',
|
|
||||||
type=models.ProductType.OCCASIONAL,
|
|
||||||
productor_id=1,
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ProductCreate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def product_update_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name='product test 1',
|
|
||||||
unit=models.Unit.PIECE,
|
|
||||||
price=10.2,
|
|
||||||
price_kg=20.4,
|
|
||||||
quantity=500,
|
|
||||||
quantity_unit='g',
|
|
||||||
type=models.ProductType.OCCASIONAL,
|
|
||||||
productor_id=1,
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ProductUpdate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def product_public_factory(productor=None, shipments=[], **kwargs):
|
|
||||||
if productor is None:
|
|
||||||
productor = productor_factory()
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name='product test 1',
|
|
||||||
unit=models.Unit.PIECE,
|
|
||||||
price=10.2,
|
|
||||||
price_kg=20.4,
|
|
||||||
quantity=500,
|
|
||||||
quantity_unit='g',
|
|
||||||
type=models.ProductType.OCCASIONAL,
|
|
||||||
productor_id=1,
|
|
||||||
productor=productor,
|
|
||||||
shipments=shipments,
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ProductPublic(**data)
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
import datetime
|
|
||||||
|
|
||||||
from src import models
|
|
||||||
|
|
||||||
|
|
||||||
def shipment_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name="test shipment",
|
|
||||||
date=datetime.date(2025, 10, 10),
|
|
||||||
form_id=1,
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.Shipment(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def shipment_public_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name="test shipment",
|
|
||||||
date=datetime.date(2025, 10, 10),
|
|
||||||
form_id=1,
|
|
||||||
products=[],
|
|
||||||
form=models.Form(id=1, name="test")
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ShipmentPublic(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def shipment_create_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name="test shipment",
|
|
||||||
form_id=1,
|
|
||||||
date='2025-10-10',
|
|
||||||
product_ids=[],
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ShipmentCreate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def shipment_update_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name="test shipment",
|
|
||||||
form_id=1,
|
|
||||||
date='2025-10-10',
|
|
||||||
product_ids=[],
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.ShipmentUpdate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def shipment_body_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name="test shipment",
|
|
||||||
form_id=1,
|
|
||||||
date="2025-10-10",
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return data
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
from src import models
|
|
||||||
|
|
||||||
|
|
||||||
def user_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name="test user",
|
|
||||||
email="test.test@test.test",
|
|
||||||
roles=[]
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.User(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def user_public_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
id=1,
|
|
||||||
name="test user",
|
|
||||||
email="test.test@test.test",
|
|
||||||
roles=[]
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.UserPublic(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def user_create_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name="test user",
|
|
||||||
email="test.test@test.test",
|
|
||||||
role_names=[],
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.UserCreate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def user_update_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name="test user",
|
|
||||||
email="test.test@test.test",
|
|
||||||
role_names=[],
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return models.UserUpdate(**data)
|
|
||||||
|
|
||||||
|
|
||||||
def user_body_factory(**kwargs):
|
|
||||||
data = dict(
|
|
||||||
name="test user",
|
|
||||||
email="test.test@test.test",
|
|
||||||
role_names=[],
|
|
||||||
)
|
|
||||||
data.update(kwargs)
|
|
||||||
return data
|
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
import datetime
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
import src.forms.service as forms_service
|
|
||||||
import src.productors.service as productors_service
|
|
||||||
import src.products.service as products_service
|
|
||||||
import src.shipments.service as shipments_service
|
|
||||||
import src.users.service as users_service
|
|
||||||
import tests.factories.forms as forms_factory
|
|
||||||
import tests.factories.productors as productors_factory
|
|
||||||
import tests.factories.products as products_factory
|
|
||||||
import tests.factories.shipments as shipments_factory
|
|
||||||
import tests.factories.users as users_factory
|
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def productor(session: Session) -> models.ProductorPublic:
|
|
||||||
productor = productors_service.create_one(
|
|
||||||
session,
|
|
||||||
productors_factory.productor_create_factory(
|
|
||||||
name='test productor',
|
|
||||||
type='Légumineuses',
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return productor
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def productors(session: Session) -> models.ProductorPublic:
|
|
||||||
productors = [
|
|
||||||
productors_service.create_one(
|
|
||||||
session,
|
|
||||||
productors_factory.productor_create_factory(
|
|
||||||
name='test productor 1',
|
|
||||||
type='Légumineuses',
|
|
||||||
)
|
|
||||||
),
|
|
||||||
productors_service.create_one(
|
|
||||||
session,
|
|
||||||
productors_factory.productor_create_factory(
|
|
||||||
name='test productor 2',
|
|
||||||
type='Légumes',
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
return productors
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def products(session: Session,
|
|
||||||
productor: models.ProductorPublic) -> list[models.ProductPublic]:
|
|
||||||
products = [
|
|
||||||
products_service.create_one(
|
|
||||||
session,
|
|
||||||
products_factory.product_create_factory(
|
|
||||||
name='product 1 occasionnal',
|
|
||||||
type=models.ProductType.OCCASIONAL,
|
|
||||||
productor_id=productor.id
|
|
||||||
)
|
|
||||||
),
|
|
||||||
products_service.create_one(
|
|
||||||
session,
|
|
||||||
products_factory.product_create_factory(
|
|
||||||
name='product 2 recurrent',
|
|
||||||
type=models.ProductType.RECCURENT,
|
|
||||||
productor_id=productor.id
|
|
||||||
)
|
|
||||||
),
|
|
||||||
]
|
|
||||||
return products
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def user(session: Session) -> models.UserPublic:
|
|
||||||
user = users_service.create_one(
|
|
||||||
session,
|
|
||||||
users_factory.user_create_factory(
|
|
||||||
name='test user',
|
|
||||||
email='test@test.com',
|
|
||||||
role_names=['Légumineuses']
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return user
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def users(session: Session) -> list[models.UserPublic]:
|
|
||||||
users = [
|
|
||||||
users_service.create_one(
|
|
||||||
session,
|
|
||||||
users_factory.user_create_factory(
|
|
||||||
name='test user 1 (admin)',
|
|
||||||
email='test1@test.com',
|
|
||||||
role_names=[
|
|
||||||
'Légumineuses',
|
|
||||||
'Légumes',
|
|
||||||
'Oeufs',
|
|
||||||
'Porc-Agneau',
|
|
||||||
'Vin',
|
|
||||||
'Fruits'])),
|
|
||||||
users_service.create_one(
|
|
||||||
session,
|
|
||||||
users_factory.user_create_factory(
|
|
||||||
name='test user 2',
|
|
||||||
email='test2@test.com',
|
|
||||||
role_names=['Légumineuses'])),
|
|
||||||
users_service.create_one(
|
|
||||||
session,
|
|
||||||
users_factory.user_create_factory(
|
|
||||||
name='test user 3',
|
|
||||||
email='test3@test.com',
|
|
||||||
role_names=['Porc-Agneau']))]
|
|
||||||
return users
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def referer(session: Session) -> models.UserPublic:
|
|
||||||
referer = users_service.create_one(
|
|
||||||
session,
|
|
||||||
users_factory.user_create_factory(
|
|
||||||
name='test referer',
|
|
||||||
email='test@test.com',
|
|
||||||
role_names=['Légumineuses'],
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return referer
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def shipments(session: Session,
|
|
||||||
forms: list[models.FormPublic],
|
|
||||||
products: list[models.ProductPublic]):
|
|
||||||
shipments = [
|
|
||||||
shipments_service.create_one(
|
|
||||||
session,
|
|
||||||
shipments_factory.shipment_create_factory(
|
|
||||||
name='test shipment 1',
|
|
||||||
date=datetime.date(2025, 10, 10),
|
|
||||||
form_id=forms[0].id,
|
|
||||||
product_ids=[p.id for p in products]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
shipments_service.create_one(
|
|
||||||
session,
|
|
||||||
shipments_factory.shipment_create_factory(
|
|
||||||
name='test shipment 2',
|
|
||||||
date=datetime.date(2025, 11, 10),
|
|
||||||
form_id=forms[0].id,
|
|
||||||
product_ids=[p.id for p in products]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
]
|
|
||||||
return shipments
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def forms(
|
|
||||||
session: Session,
|
|
||||||
productor: models.ProductorPublic,
|
|
||||||
referer: models.UserPublic
|
|
||||||
) -> list[models.FormPublic]:
|
|
||||||
forms = [
|
|
||||||
forms_service.create_one(
|
|
||||||
session,
|
|
||||||
forms_factory.form_create_factory(
|
|
||||||
name='test form 1',
|
|
||||||
productor_id=productor.id,
|
|
||||||
referer_id=referer.id,
|
|
||||||
season='test season 1',
|
|
||||||
)
|
|
||||||
),
|
|
||||||
forms_service.create_one(
|
|
||||||
session,
|
|
||||||
forms_factory.form_create_factory(
|
|
||||||
name='test form 2',
|
|
||||||
productor_id=productor.id,
|
|
||||||
referer_id=referer.id,
|
|
||||||
season='test season 2',
|
|
||||||
)
|
|
||||||
)
|
|
||||||
]
|
|
||||||
return forms
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
import src.contracts.service as service
|
|
||||||
import tests.factories.contract_products as contract_products_factory
|
|
||||||
import tests.factories.contracts as contract_factory
|
|
||||||
import tests.factories.forms as form_factory
|
|
||||||
from fastapi.exceptions import HTTPException
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.main import app
|
|
||||||
|
|
||||||
|
|
||||||
class TestContracts:
|
|
||||||
def test_get_all(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
contract_factory.contract_public_factory(id=1),
|
|
||||||
contract_factory.contract_public_factory(id=2),
|
|
||||||
contract_factory.contract_public_factory(id=3),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/contracts')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 1
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
mock_user,
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_filters(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
contract_factory.contract_public_factory(id=2),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/contracts?forms=form test')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 2
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
mock_user,
|
|
||||||
['form test'],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.contracts.service.get_all')
|
|
||||||
response = client.get('/api/contracts')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_get_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = contract_factory.contract_public_factory(id=2)
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'is_allowed',
|
|
||||||
return_value=True
|
|
||||||
)
|
|
||||||
response = client.get('/api/contracts/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['id'] == 2
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_one_notfound(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = None
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
|
|
||||||
mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'is_allowed',
|
|
||||||
return_value=True
|
|
||||||
)
|
|
||||||
response = client.get('/api/contracts/2')
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.contracts.service.get_one')
|
|
||||||
response = client.get('/api/contracts/2')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_delete_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
contract_result = contract_factory.contract_public_factory()
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'delete_one',
|
|
||||||
return_value=contract_result
|
|
||||||
)
|
|
||||||
|
|
||||||
mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'is_allowed',
|
|
||||||
return_value=True
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.delete('/api/contracts/2')
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_notfound(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user
|
|
||||||
):
|
|
||||||
contract_result = None
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'delete_one',
|
|
||||||
return_value=contract_result
|
|
||||||
)
|
|
||||||
|
|
||||||
mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'is_allowed',
|
|
||||||
return_value=True
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.delete('/api/contracts/2')
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user
|
|
||||||
):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.contracts.service.delete_one')
|
|
||||||
response = client.delete('/api/contracts/2')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
@@ -1,355 +0,0 @@
|
|||||||
import src.forms.exceptions as forms_exceptions
|
|
||||||
import src.forms.service as service
|
|
||||||
import src.messages as messages
|
|
||||||
import tests.factories.forms as form_factory
|
|
||||||
from fastapi.exceptions import HTTPException
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.main import app
|
|
||||||
|
|
||||||
|
|
||||||
class TestForms:
|
|
||||||
def test_get_all(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
form_factory.form_public_factory(name="test 1", id=1),
|
|
||||||
form_factory.form_public_factory(name="test 2", id=2),
|
|
||||||
form_factory.form_public_factory(name="test 3", id=3),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/forms/referents')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 1
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
False,
|
|
||||||
mock_user,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_filters(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
form_factory.form_public_factory(name="test 2", id=2),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get(
|
|
||||||
'/api/forms/referents?current_season=true&seasons=hiver-2025&productors=test productor')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 2
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
['hiver-2025'],
|
|
||||||
['test productor'],
|
|
||||||
True,
|
|
||||||
mock_user,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.forms.service.get_all')
|
|
||||||
response = client.get('/api/forms/referents')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_get_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = form_factory.form_public_factory(name="test 2", id=2)
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/forms/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['id'] == 2
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_one_notfound(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = None
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
response = client.get('/api/forms/2')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
form_body = form_factory.form_body_factory(name='test form create')
|
|
||||||
form_create = form_factory.form_create_factory(name='test form create')
|
|
||||||
form_result = form_factory.form_public_factory(name='test form create')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'create_one',
|
|
||||||
return_value=form_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.post('/api/forms', json=form_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test form create'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
form_create
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_one_referer_notfound(
|
|
||||||
self, client, mocker, mock_session, mock_user):
|
|
||||||
form_body = form_factory.form_body_factory(
|
|
||||||
name='test form create', referer_id=12312)
|
|
||||||
form_create = form_factory.form_create_factory(
|
|
||||||
name='test form create', referer_id=12312)
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service, 'create_one', side_effect=forms_exceptions.UserNotFoundError(
|
|
||||||
messages.Messages.not_found('referer')))
|
|
||||||
|
|
||||||
response = client.post('/api/forms', json=form_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
form_create
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_one_productor_notfound(
|
|
||||||
self, client, mocker, mock_session, mock_user):
|
|
||||||
form_body = form_factory.form_body_factory(
|
|
||||||
name='test form create', productor_id=1231)
|
|
||||||
form_create = form_factory.form_create_factory(
|
|
||||||
name='test form create', productor_id=1231)
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service, 'create_one', side_effect=forms_exceptions.ProductorNotFoundError(
|
|
||||||
messages.Messages.not_found('productor')))
|
|
||||||
|
|
||||||
response = client.post('/api/forms', json=form_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
form_create
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
form_body = form_factory.form_body_factory(name='test form create')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.forms.service.create_one')
|
|
||||||
response = client.post('/api/forms', json=form_body)
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_update_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
form_body = form_factory.form_body_factory(name='test form update')
|
|
||||||
form_update = form_factory.form_update_factory(name='test form update')
|
|
||||||
form_result = form_factory.form_public_factory(name='test form update')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'update_one',
|
|
||||||
return_value=form_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.put('/api/forms/2', json=form_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test form update'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
form_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_notfound(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
form_body = form_factory.form_body_factory(name='test form update')
|
|
||||||
form_update = form_factory.form_update_factory(name='test form update')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service, 'update_one', side_effect=forms_exceptions.FormNotFoundError(
|
|
||||||
messages.Messages.not_found('form')))
|
|
||||||
|
|
||||||
response = client.put('/api/forms/2', json=form_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
form_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_referer_notfound(
|
|
||||||
self, client, mocker, mock_session, mock_user):
|
|
||||||
form_body = form_factory.form_body_factory(name='test form update')
|
|
||||||
form_update = form_factory.form_update_factory(name='test form update')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service, 'update_one', side_effect=forms_exceptions.UserNotFoundError(
|
|
||||||
messages.Messages.not_found('referer')))
|
|
||||||
|
|
||||||
response = client.put('/api/forms/2', json=form_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
form_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_productor_notfound(
|
|
||||||
self, client, mocker, mock_session, mock_user):
|
|
||||||
form_body = form_factory.form_body_factory(name='test form update')
|
|
||||||
form_update = form_factory.form_update_factory(name='test form update')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service, 'update_one', side_effect=forms_exceptions.ProductorNotFoundError(
|
|
||||||
messages.Messages.not_found('productor')))
|
|
||||||
|
|
||||||
response = client.put('/api/forms/2', json=form_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
form_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
form_body = form_factory.form_body_factory(name='test form update')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.forms.service.update_one')
|
|
||||||
response = client.put('/api/forms/2', json=form_body)
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_delete_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
form_result = form_factory.form_public_factory(name='test form delete')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'delete_one',
|
|
||||||
return_value=form_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.delete('/api/forms/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test form delete'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_notfound(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
form_result = None
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service, 'delete_one', side_effect=forms_exceptions.FormNotFoundError(
|
|
||||||
messages.Messages.not_found('form')))
|
|
||||||
|
|
||||||
response = client.delete('/api/forms/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.forms.service.delete_one')
|
|
||||||
response = client.delete('/api/forms/2')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
import src.messages as messages
|
|
||||||
import src.productors.exceptions as exceptions
|
|
||||||
import src.productors.service as service
|
|
||||||
import tests.factories.productors as productor_factory
|
|
||||||
from fastapi.exceptions import HTTPException
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.main import app
|
|
||||||
|
|
||||||
|
|
||||||
class TestProductors:
|
|
||||||
def test_get_all(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
productor_factory.productor_public_factory(name="test 1", id=1),
|
|
||||||
productor_factory.productor_public_factory(name="test 2", id=2),
|
|
||||||
productor_factory.productor_public_factory(name="test 3", id=3),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/productors')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 1
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
mock_user,
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_filters(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
productor_factory.productor_public_factory(name="test 2", id=2),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get(
|
|
||||||
'/api/productors?types=Légumineuses&names=test 2')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 2
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
mock_user,
|
|
||||||
['test 2'],
|
|
||||||
['Légumineuses'],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.productors.service.get_all')
|
|
||||||
response = client.get('/api/productors')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_get_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = productor_factory.productor_public_factory(
|
|
||||||
name="test 2", id=2)
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/productors/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['id'] == 2
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_one_notfound(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = None
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
response = client.get('/api/productors/2')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.productors.service.get_one')
|
|
||||||
response = client.get('/api/productors/2')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_create_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
productor_body = productor_factory.productor_body_factory(
|
|
||||||
name='test productor create')
|
|
||||||
productor_create = productor_factory.productor_create_factory(
|
|
||||||
name='test productor create')
|
|
||||||
productor_result = productor_factory.productor_public_factory(
|
|
||||||
name='test productor create')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'create_one',
|
|
||||||
return_value=productor_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.post('/api/productors', json=productor_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test productor create'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
productor_create
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
productor_body = productor_factory.productor_body_factory(
|
|
||||||
name='test productor create')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.productors.service.create_one')
|
|
||||||
response = client.post('/api/productors', json=productor_body)
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_update_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
productor_body = productor_factory.productor_body_factory(
|
|
||||||
name='test productor update')
|
|
||||||
productor_update = productor_factory.productor_update_factory(
|
|
||||||
name='test productor update')
|
|
||||||
productor_result = productor_factory.productor_public_factory(
|
|
||||||
name='test productor update')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'update_one',
|
|
||||||
return_value=productor_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.put('/api/productors/2', json=productor_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test productor update'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
productor_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_notfound(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
productor_body = productor_factory.productor_body_factory(
|
|
||||||
name='test productor update')
|
|
||||||
productor_update = productor_factory.productor_update_factory(
|
|
||||||
name='test productor update')
|
|
||||||
productor_result = None
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service, 'update_one', side_effect=exceptions.ProductorNotFoundError(
|
|
||||||
messages.Messages.not_found('productor')))
|
|
||||||
|
|
||||||
response = client.put('/api/productors/2', json=productor_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
productor_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
productor_body = productor_factory.productor_body_factory(
|
|
||||||
name='test productor update')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.productors.service.update_one')
|
|
||||||
response = client.put('/api/productors/2', json=productor_body)
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_delete_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
productor_result = productor_factory.productor_public_factory(
|
|
||||||
name='test productor delete')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'delete_one',
|
|
||||||
return_value=productor_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.delete('/api/productors/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test productor delete'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_notfound(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
productor_result = None
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service, 'delete_one', side_effect=exceptions.ProductorNotFoundError(
|
|
||||||
messages.Messages.not_found('productor')))
|
|
||||||
|
|
||||||
response = client.delete('/api/productors/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
productor_body = productor_factory.productor_body_factory(
|
|
||||||
name='test productor delete')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.productors.service.delete_one')
|
|
||||||
response = client.delete('/api/productors/2')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
@@ -1,313 +0,0 @@
|
|||||||
import src.products.exceptions as exceptions
|
|
||||||
import src.products.service as service
|
|
||||||
import tests.factories.products as product_factory
|
|
||||||
from fastapi.exceptions import HTTPException
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.main import app
|
|
||||||
|
|
||||||
|
|
||||||
class TestProducts:
|
|
||||||
def test_get_all(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
product_factory.product_public_factory(name="test 1", id=1),
|
|
||||||
product_factory.product_public_factory(name="test 2", id=2),
|
|
||||||
product_factory.product_public_factory(name="test 3", id=3),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/products')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 1
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
mock_user,
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_filters(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
product_factory.product_public_factory(name="test 2", id=2),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/products?types=1&names=test 2')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 2
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
mock_user,
|
|
||||||
['test 2'],
|
|
||||||
[],
|
|
||||||
['1'],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.products.service.get_all')
|
|
||||||
response = client.get('/api/products')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_get_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = product_factory.product_public_factory(
|
|
||||||
name="test 2", id=2)
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/products/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['id'] == 2
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_one_notfound(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = None
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
response = client.get('/api/products/2')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.products.service.get_one')
|
|
||||||
response = client.get('/api/products/2')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_create_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
product_body = product_factory.product_body_factory(
|
|
||||||
name='test product create')
|
|
||||||
product_create = product_factory.product_create_factory(
|
|
||||||
name='test product create')
|
|
||||||
product_result = product_factory.product_public_factory(
|
|
||||||
name='test product create')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'create_one',
|
|
||||||
return_value=product_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.post('/api/products', json=product_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test product create'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
product_create
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
product_body = product_factory.product_body_factory(
|
|
||||||
name='test product create')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.products.service.create_one')
|
|
||||||
response = client.post('/api/products', json=product_body)
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_update_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
product_body = product_factory.product_body_factory(
|
|
||||||
name='test product update')
|
|
||||||
product_update = product_factory.product_update_factory(
|
|
||||||
name='test product update')
|
|
||||||
product_result = product_factory.product_public_factory(
|
|
||||||
name='test product update')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'update_one',
|
|
||||||
return_value=product_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.put('/api/products/2', json=product_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test product update'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
product_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_notfound(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
product_body = product_factory.product_body_factory(
|
|
||||||
name='test product update')
|
|
||||||
product_update = product_factory.product_update_factory(
|
|
||||||
name='test product update')
|
|
||||||
product_result = None
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'update_one',
|
|
||||||
side_effect=exceptions.ProductNotFoundError('Product not found')
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.put('/api/products/2', json=product_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
product_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
product_body = product_factory.product_body_factory(
|
|
||||||
name='test product update')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.products.service.update_one')
|
|
||||||
response = client.put('/api/products/2', json=product_body)
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_delete_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
product_result = product_factory.product_public_factory(
|
|
||||||
name='test product delete')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'delete_one',
|
|
||||||
return_value=product_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.delete('/api/products/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test product delete'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_notfound(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
product_result = None
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'delete_one',
|
|
||||||
side_effect=exceptions.ProductNotFoundError('Product not found')
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.delete('/api/products/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
product_body = product_factory.product_body_factory(
|
|
||||||
name='test product delete')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.products.service.delete_one')
|
|
||||||
response = client.delete('/api/products/2')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
@@ -1,310 +0,0 @@
|
|||||||
import src.messages as messages
|
|
||||||
import src.shipments.exceptions as exceptions
|
|
||||||
import src.shipments.service as service
|
|
||||||
import tests.factories.shipments as shipment_factory
|
|
||||||
from fastapi.exceptions import HTTPException
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.main import app
|
|
||||||
|
|
||||||
|
|
||||||
class TestShipments:
|
|
||||||
def test_get_all(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
shipment_factory.shipment_public_factory(name="test 1", id=1),
|
|
||||||
shipment_factory.shipment_public_factory(name="test 2", id=2),
|
|
||||||
shipment_factory.shipment_public_factory(name="test 3", id=3),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/shipments')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 1
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
mock_user,
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_filters(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
shipment_factory.shipment_public_factory(name="test 2", id=2),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get(
|
|
||||||
'/api/shipments?dates=2025-10-10&names=test 2&forms=contract form 1')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 2
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
mock_user,
|
|
||||||
['test 2'],
|
|
||||||
['2025-10-10'],
|
|
||||||
['contract form 1'],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.shipments.service.get_all')
|
|
||||||
response = client.get('/api/shipments')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_get_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = shipment_factory.shipment_public_factory(
|
|
||||||
name="test 2", id=2)
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/shipments/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['id'] == 2
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_one_notfound(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = None
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
response = client.get('/api/shipments/2')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.shipments.service.get_one')
|
|
||||||
response = client.get('/api/shipments/2')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_create_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
shipment_body = shipment_factory.shipment_body_factory(
|
|
||||||
name='test shipment create')
|
|
||||||
shipment_create = shipment_factory.shipment_create_factory(
|
|
||||||
name='test shipment create')
|
|
||||||
shipment_result = shipment_factory.shipment_public_factory(
|
|
||||||
name='test shipment create')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'create_one',
|
|
||||||
return_value=shipment_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.post('/api/shipments', json=shipment_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test shipment create'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
shipment_create
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
shipment_body = shipment_factory.shipment_body_factory(
|
|
||||||
name='test shipment create')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.shipments.service.create_one')
|
|
||||||
response = client.post('/api/shipments', json=shipment_body)
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_update_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
shipment_body = shipment_factory.shipment_body_factory(
|
|
||||||
name='test shipment update')
|
|
||||||
shipment_update = shipment_factory.shipment_update_factory(
|
|
||||||
name='test shipment update')
|
|
||||||
shipment_result = shipment_factory.shipment_public_factory(
|
|
||||||
name='test shipment update')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'update_one',
|
|
||||||
return_value=shipment_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.put('/api/shipments/2', json=shipment_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test shipment update'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
shipment_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_notfound(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
shipment_body = shipment_factory.shipment_body_factory(
|
|
||||||
name='test shipment update')
|
|
||||||
shipment_update = shipment_factory.shipment_update_factory(
|
|
||||||
name='test shipment update')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service, 'update_one', side_effect=exceptions.ShipmentNotFoundError(
|
|
||||||
messages.Messages.not_found('shipment')))
|
|
||||||
|
|
||||||
response = client.put('/api/shipments/2', json=shipment_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
shipment_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
shipment_body = shipment_factory.shipment_body_factory(
|
|
||||||
name='test shipment update')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.shipments.service.update_one')
|
|
||||||
response = client.put('/api/shipments/2', json=shipment_body)
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_delete_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
shipment_result = shipment_factory.shipment_public_factory(
|
|
||||||
name='test shipment delete')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'delete_one',
|
|
||||||
return_value=shipment_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.delete('/api/shipments/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test shipment delete'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_notfound(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
shipment_result = None
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service, 'delete_one', side_effect=exceptions.ShipmentNotFoundError(
|
|
||||||
messages.Messages.not_found('shipment')))
|
|
||||||
|
|
||||||
response = client.delete('/api/shipments/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
shipment_body = shipment_factory.shipment_body_factory(
|
|
||||||
name='test shipment delete')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.shipments.service.delete_one')
|
|
||||||
response = client.delete('/api/shipments/2')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
import src.users.exceptions as exceptions
|
|
||||||
import src.users.service as service
|
|
||||||
import tests.factories.users as user_factory
|
|
||||||
from fastapi.exceptions import HTTPException
|
|
||||||
from src import models
|
|
||||||
from src.auth.auth import get_current_user
|
|
||||||
from src.main import app
|
|
||||||
|
|
||||||
|
|
||||||
class TestUsers:
|
|
||||||
def test_get_all(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
user_factory.user_public_factory(name="test 1", id=1),
|
|
||||||
user_factory.user_public_factory(name="test 2", id=2),
|
|
||||||
user_factory.user_public_factory(name="test 3", id=3),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/users')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 1
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_filters(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_results = [
|
|
||||||
user_factory.user_public_factory(name="test 2", id=2),
|
|
||||||
]
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_all',
|
|
||||||
return_value=mock_results
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/users?emails=test@test.test&names=test 2')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data[0]['id'] == 2
|
|
||||||
assert len(response_data) == len(mock_results)
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
['test 2'],
|
|
||||||
['test@test.test'],
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_all_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.users.service.get_all')
|
|
||||||
response = client.get('/api/users')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_get_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = user_factory.user_public_factory(name="test 2", id=2)
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.get('/api/users/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['id'] == 2
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_one_notfound(self, client, mocker, mock_session, mock_user):
|
|
||||||
mock_result = None
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'get_one',
|
|
||||||
return_value=mock_result
|
|
||||||
)
|
|
||||||
response = client.get('/api/users/2')
|
|
||||||
response_data = response.json()
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_get_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.users.service.get_one')
|
|
||||||
response = client.get('/api/users/2')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_create_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
user_body = user_factory.user_body_factory(name='test user create')
|
|
||||||
user_create = user_factory.user_create_factory(name='test user create')
|
|
||||||
user_result = user_factory.user_public_factory(name='test user create')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'create_one',
|
|
||||||
return_value=user_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.post('/api/users', json=user_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test user create'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
user_create
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_create_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
user_body = user_factory.user_body_factory(name='test user create')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.users.service.create_one')
|
|
||||||
response = client.post('/api/users', json=user_body)
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_update_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
user_body = user_factory.user_body_factory(name='test user update')
|
|
||||||
user_update = user_factory.user_update_factory(name='test user update')
|
|
||||||
user_result = user_factory.user_public_factory(name='test user update')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'update_one',
|
|
||||||
return_value=user_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.put('/api/users/2', json=user_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test user update'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
user_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_notfound(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
user_body = user_factory.user_body_factory(name='test user update')
|
|
||||||
user_update = user_factory.user_update_factory(name='test user update')
|
|
||||||
user_result = None
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'update_one',
|
|
||||||
side_effect=exceptions.UserNotFoundError('User 2 not found')
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.put('/api/users/2', json=user_body)
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
user_update
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_update_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
user_body = user_factory.user_body_factory(name='test user update')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.users.service.update_one')
|
|
||||||
response = client.put('/api/users/2', json=user_body)
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
|
|
||||||
def test_delete_one(self, client, mocker, mock_session, mock_user):
|
|
||||||
user_result = user_factory.user_public_factory(name='test user delete')
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'delete_one',
|
|
||||||
return_value=user_result
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.delete('/api/users/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 200
|
|
||||||
assert response_data['name'] == 'test user delete'
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_notfound(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
user_result = None
|
|
||||||
|
|
||||||
mock = mocker.patch.object(
|
|
||||||
service,
|
|
||||||
'delete_one',
|
|
||||||
side_effect=exceptions.UserNotFoundError('User 2 not found')
|
|
||||||
)
|
|
||||||
|
|
||||||
response = client.delete('/api/users/2')
|
|
||||||
response_data = response.json()
|
|
||||||
|
|
||||||
assert response.status_code == 404
|
|
||||||
mock.assert_called_once_with(
|
|
||||||
mock_session,
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_one_unauthorized(
|
|
||||||
self,
|
|
||||||
client,
|
|
||||||
mocker,
|
|
||||||
mock_session,
|
|
||||||
mock_user):
|
|
||||||
def unauthorized():
|
|
||||||
raise HTTPException(status_code=401)
|
|
||||||
user_body = user_factory.user_body_factory(name='test user delete')
|
|
||||||
|
|
||||||
app.dependency_overrides[get_current_user] = unauthorized
|
|
||||||
|
|
||||||
mock = mocker.patch('src.users.service.delete_one')
|
|
||||||
response = client.delete('/api/users/2')
|
|
||||||
|
|
||||||
assert response.status_code == 401
|
|
||||||
mock.assert_not_called()
|
|
||||||
|
|
||||||
app.dependency_overrides.clear()
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import src.forms.exceptions as forms_exceptions
|
|
||||||
import src.forms.service as forms_service
|
|
||||||
import tests.factories.forms as forms_factory
|
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
|
|
||||||
|
|
||||||
class TestFormsService:
|
|
||||||
def test_get_all_forms(self, session: Session,
|
|
||||||
forms: list[models.FormPublic]):
|
|
||||||
result = forms_service.get_all(session, [], [], False)
|
|
||||||
|
|
||||||
assert len(result) == 2
|
|
||||||
assert result == forms
|
|
||||||
|
|
||||||
def test_get_all_forms_filter_productors(
|
|
||||||
self, session: Session, forms: list[models.FormPublic]):
|
|
||||||
result = forms_service.get_all(session, [], ['test productor'], False)
|
|
||||||
|
|
||||||
assert len(result) == 2
|
|
||||||
assert result == forms
|
|
||||||
|
|
||||||
def test_get_all_forms_filter_season(
|
|
||||||
self, session: Session, forms: list[models.FormPublic]):
|
|
||||||
result = forms_service.get_all(session, ['test season 1'], [], False)
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
|
|
||||||
def test_get_all_forms_all_filters(
|
|
||||||
self, session: Session, forms: list[models.FormPublic]):
|
|
||||||
result = forms_service.get_all(
|
|
||||||
session, ['test season 1'], ['test productor'], True)
|
|
||||||
|
|
||||||
assert result == forms
|
|
||||||
|
|
||||||
def test_get_one_form(self, session: Session,
|
|
||||||
forms: list[models.FormPublic]):
|
|
||||||
result = forms_service.get_one(session, forms[0].id)
|
|
||||||
|
|
||||||
assert result == forms[0]
|
|
||||||
|
|
||||||
def test_get_one_form_notfound(self, session: Session):
|
|
||||||
result = forms_service.get_one(session, 122)
|
|
||||||
|
|
||||||
assert result is None
|
|
||||||
|
|
||||||
def test_create_form(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productor: models.ProductorPublic,
|
|
||||||
referer: models.ProductorPublic
|
|
||||||
):
|
|
||||||
form_create = forms_factory.form_create_factory(
|
|
||||||
name="new test form",
|
|
||||||
productor_id=productor.id,
|
|
||||||
referer=referer.id,
|
|
||||||
season="new test season",
|
|
||||||
)
|
|
||||||
result = forms_service.create_one(session, form_create)
|
|
||||||
|
|
||||||
assert result.id is not None
|
|
||||||
assert result.name == "new test form"
|
|
||||||
assert result.productor.name == "test productor"
|
|
||||||
|
|
||||||
def test_create_form_invalidinput(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productor: models.Productor
|
|
||||||
):
|
|
||||||
form_create = None
|
|
||||||
with pytest.raises(forms_exceptions.FormCreateError):
|
|
||||||
result = forms_service.create_one(session, form_create)
|
|
||||||
|
|
||||||
form_create = forms_factory.form_create_factory(productor_id=123)
|
|
||||||
with pytest.raises(forms_exceptions.ProductorNotFoundError):
|
|
||||||
result = forms_service.create_one(session, form_create)
|
|
||||||
|
|
||||||
form_create = forms_factory.form_create_factory(
|
|
||||||
productor_id=productor.id,
|
|
||||||
referer_id=123
|
|
||||||
)
|
|
||||||
with pytest.raises(forms_exceptions.UserNotFoundError):
|
|
||||||
result = forms_service.create_one(session, form_create)
|
|
||||||
|
|
||||||
def test_update_form(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productor: models.ProductorPublic,
|
|
||||||
referer: models.ProductorPublic,
|
|
||||||
forms: list[models.FormPublic]
|
|
||||||
):
|
|
||||||
form_update = forms_factory.form_update_factory(
|
|
||||||
name='updated test form',
|
|
||||||
productor_id=productor.id,
|
|
||||||
referer_id=referer.id,
|
|
||||||
season='updated test season'
|
|
||||||
)
|
|
||||||
form_id = forms[0].id
|
|
||||||
result = forms_service.update_one(session, form_id, form_update)
|
|
||||||
|
|
||||||
assert result.id == form_id
|
|
||||||
assert result.name == 'updated test form'
|
|
||||||
assert result.season == 'updated test season'
|
|
||||||
|
|
||||||
def test_update_form_notfound(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productor: models.ProductorPublic,
|
|
||||||
referer: models.ProductorPublic,
|
|
||||||
):
|
|
||||||
form_update = forms_factory.form_update_factory(
|
|
||||||
name='updated test form',
|
|
||||||
productor_id=productor.id,
|
|
||||||
referer_id=referer.id,
|
|
||||||
season='updated test season'
|
|
||||||
)
|
|
||||||
form_id = 123
|
|
||||||
with pytest.raises(forms_exceptions.FormNotFoundError):
|
|
||||||
result = forms_service.update_one(session, form_id, form_update)
|
|
||||||
|
|
||||||
def test_update_form_invalidinput(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productor: models.ProductorPublic,
|
|
||||||
forms: list[models.FormPublic]
|
|
||||||
):
|
|
||||||
form_id = forms[0].id
|
|
||||||
form_update = forms_factory.form_update_factory(productor_id=123)
|
|
||||||
with pytest.raises(forms_exceptions.ProductorNotFoundError):
|
|
||||||
result = forms_service.update_one(session, form_id, form_update)
|
|
||||||
|
|
||||||
form_update = forms_factory.form_update_factory(
|
|
||||||
productor_id=productor.id,
|
|
||||||
referer_id=123
|
|
||||||
)
|
|
||||||
with pytest.raises(forms_exceptions.UserNotFoundError):
|
|
||||||
result = forms_service.update_one(session, form_id, form_update)
|
|
||||||
|
|
||||||
def test_delete_form(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
forms: list[models.FormPublic]
|
|
||||||
):
|
|
||||||
form_id = forms[0].id
|
|
||||||
result = forms_service.delete_one(session, form_id)
|
|
||||||
|
|
||||||
check = forms_service.get_one(session, form_id)
|
|
||||||
assert check is None
|
|
||||||
|
|
||||||
def test_delete_form_notfound(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
forms: list[models.FormPublic]
|
|
||||||
):
|
|
||||||
form_id = 123
|
|
||||||
with pytest.raises(forms_exceptions.FormNotFoundError):
|
|
||||||
result = forms_service.delete_one(session, form_id)
|
|
||||||
@@ -1,146 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import src.productors.exceptions as productors_exceptions
|
|
||||||
import src.productors.service as productors_service
|
|
||||||
import tests.factories.productors as productors_factory
|
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
|
|
||||||
|
|
||||||
class TestProductorsService:
|
|
||||||
def test_get_all_productors(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productors: list[models.ProductorPublic],
|
|
||||||
user: models.UserPublic
|
|
||||||
):
|
|
||||||
result = productors_service.get_all(session, user, [], [])
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
assert result == [productors[0]]
|
|
||||||
|
|
||||||
def test_get_all_productors_filter_names(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productors: list[models.ProductorPublic],
|
|
||||||
user: models.UserPublic
|
|
||||||
):
|
|
||||||
result = productors_service.get_all(
|
|
||||||
session,
|
|
||||||
user,
|
|
||||||
['test productor 1'],
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
|
|
||||||
def test_get_all_productors_filter_types(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productors: list[models.ProductorPublic],
|
|
||||||
user: models.UserPublic
|
|
||||||
):
|
|
||||||
result = productors_service.get_all(
|
|
||||||
session,
|
|
||||||
user,
|
|
||||||
[],
|
|
||||||
['Légumineuses'],
|
|
||||||
)
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
|
|
||||||
def test_get_all_productors_all_filters(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productors: list[models.ProductorPublic],
|
|
||||||
user: models.UserPublic
|
|
||||||
):
|
|
||||||
result = productors_service.get_all(
|
|
||||||
session,
|
|
||||||
user,
|
|
||||||
['test productor 1'],
|
|
||||||
['Légumineuses'],
|
|
||||||
)
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
|
|
||||||
def test_get_one_productor(self,
|
|
||||||
session: Session,
|
|
||||||
productors: list[models.ProductorPublic]):
|
|
||||||
result = productors_service.get_one(session, productors[0].id)
|
|
||||||
|
|
||||||
assert result == productors[0]
|
|
||||||
|
|
||||||
def test_get_one_productor_notfound(self, session: Session):
|
|
||||||
result = productors_service.get_one(session, 122)
|
|
||||||
|
|
||||||
assert result is None
|
|
||||||
|
|
||||||
def test_create_productor(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
referer: models.ProductorPublic
|
|
||||||
):
|
|
||||||
productor_create = productors_factory.productor_create_factory(
|
|
||||||
name="new test productor",
|
|
||||||
)
|
|
||||||
result = productors_service.create_one(session, productor_create)
|
|
||||||
|
|
||||||
assert result.id is not None
|
|
||||||
assert result.name == "new test productor"
|
|
||||||
|
|
||||||
def test_create_productor_invalidinput(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
):
|
|
||||||
productor_create = None
|
|
||||||
with pytest.raises(productors_exceptions.ProductorCreateError):
|
|
||||||
result = productors_service.create_one(session, productor_create)
|
|
||||||
|
|
||||||
def test_update_productor(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
referer: models.ProductorPublic,
|
|
||||||
productors: list[models.ProductorPublic]
|
|
||||||
):
|
|
||||||
productor_update = productors_factory.productor_update_factory(
|
|
||||||
name='updated test productor',
|
|
||||||
)
|
|
||||||
productor_id = productors[0].id
|
|
||||||
result = productors_service.update_one(
|
|
||||||
session, productor_id, productor_update)
|
|
||||||
|
|
||||||
assert result.id == productor_id
|
|
||||||
assert result.name == 'updated test productor'
|
|
||||||
|
|
||||||
def test_update_productor_notfound(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
referer: models.ProductorPublic,
|
|
||||||
):
|
|
||||||
productor_update = productors_factory.productor_update_factory(
|
|
||||||
name='updated test productor',
|
|
||||||
)
|
|
||||||
productor_id = 123
|
|
||||||
with pytest.raises(productors_exceptions.ProductorNotFoundError):
|
|
||||||
result = productors_service.update_one(
|
|
||||||
session, productor_id, productor_update)
|
|
||||||
|
|
||||||
def test_delete_productor(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productors: list[models.ProductorPublic]
|
|
||||||
):
|
|
||||||
productor_id = productors[0].id
|
|
||||||
result = productors_service.delete_one(session, productor_id)
|
|
||||||
|
|
||||||
check = productors_service.get_one(session, productor_id)
|
|
||||||
assert check is None
|
|
||||||
|
|
||||||
def test_delete_productor_notfound(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productors: list[models.ProductorPublic]
|
|
||||||
):
|
|
||||||
productor_id = 123
|
|
||||||
with pytest.raises(productors_exceptions.ProductorNotFoundError):
|
|
||||||
result = productors_service.delete_one(session, productor_id)
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import src.products.exceptions as products_exceptions
|
|
||||||
import src.products.service as products_service
|
|
||||||
import tests.factories.products as products_factory
|
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
|
|
||||||
|
|
||||||
class TestProductsService:
|
|
||||||
def test_get_all_products(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
products: list[models.ProductPublic],
|
|
||||||
user: models.UserPublic
|
|
||||||
):
|
|
||||||
result = products_service.get_all(session, user, [], [], [])
|
|
||||||
|
|
||||||
assert len(result) == 2
|
|
||||||
assert result == products
|
|
||||||
|
|
||||||
def test_get_all_products_filter_productors(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
products: list[models.ProductPublic],
|
|
||||||
user: models.UserPublic
|
|
||||||
):
|
|
||||||
result = products_service.get_all(
|
|
||||||
session,
|
|
||||||
user,
|
|
||||||
[],
|
|
||||||
['test productor'],
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
assert len(result) == 2
|
|
||||||
assert result == products
|
|
||||||
|
|
||||||
def test_get_all_products_filter_names(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
products: list[models.ProductPublic],
|
|
||||||
user: models.UserPublic
|
|
||||||
):
|
|
||||||
result = products_service.get_all(
|
|
||||||
session,
|
|
||||||
user,
|
|
||||||
['product 1 occasionnal'],
|
|
||||||
[],
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
|
|
||||||
def test_get_all_products_filter_types(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
products: list[models.ProductPublic],
|
|
||||||
user: models.UserPublic
|
|
||||||
):
|
|
||||||
result = products_service.get_all(
|
|
||||||
session,
|
|
||||||
user,
|
|
||||||
[],
|
|
||||||
[],
|
|
||||||
['1']
|
|
||||||
)
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
|
|
||||||
def test_get_all_products_all_filters(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
products: list[models.ProductPublic],
|
|
||||||
user: models.UserPublic
|
|
||||||
):
|
|
||||||
result = products_service.get_all(
|
|
||||||
session,
|
|
||||||
user,
|
|
||||||
['product 1 occasionnal'],
|
|
||||||
['test productor'],
|
|
||||||
['1']
|
|
||||||
)
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
|
|
||||||
def test_get_one_product(self, session: Session,
|
|
||||||
products: list[models.ProductPublic]):
|
|
||||||
result = products_service.get_one(session, products[0].id)
|
|
||||||
|
|
||||||
assert result == products[0]
|
|
||||||
|
|
||||||
def test_get_one_product_notfound(self, session: Session):
|
|
||||||
result = products_service.get_one(session, 122)
|
|
||||||
|
|
||||||
assert result is None
|
|
||||||
|
|
||||||
def test_create_product(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productor: models.ProductorPublic,
|
|
||||||
referer: models.ProductorPublic
|
|
||||||
):
|
|
||||||
product_create = products_factory.product_create_factory(
|
|
||||||
name="new test product",
|
|
||||||
productor_id=productor.id,
|
|
||||||
)
|
|
||||||
result = products_service.create_one(session, product_create)
|
|
||||||
|
|
||||||
assert result.id is not None
|
|
||||||
assert result.name == "new test product"
|
|
||||||
assert result.productor.name == "test productor"
|
|
||||||
|
|
||||||
def test_create_product_invalidinput(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productor: models.Productor
|
|
||||||
):
|
|
||||||
product_create = None
|
|
||||||
with pytest.raises(products_exceptions.ProductCreateError):
|
|
||||||
result = products_service.create_one(session, product_create)
|
|
||||||
|
|
||||||
product_create = products_factory.product_create_factory(
|
|
||||||
productor_id=123)
|
|
||||||
with pytest.raises(products_exceptions.ProductorNotFoundError):
|
|
||||||
result = products_service.create_one(session, product_create)
|
|
||||||
|
|
||||||
def test_update_product(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productor: models.ProductorPublic,
|
|
||||||
referer: models.ProductorPublic,
|
|
||||||
products: list[models.ProductPublic]
|
|
||||||
):
|
|
||||||
product_update = products_factory.product_update_factory(
|
|
||||||
name='updated test product',
|
|
||||||
productor_id=productor.id,
|
|
||||||
)
|
|
||||||
product_id = products[0].id
|
|
||||||
result = products_service.update_one(
|
|
||||||
session, product_id, product_update)
|
|
||||||
|
|
||||||
assert result.id == product_id
|
|
||||||
assert result.name == 'updated test product'
|
|
||||||
|
|
||||||
def test_update_product_notfound(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productor: models.ProductorPublic,
|
|
||||||
referer: models.ProductorPublic,
|
|
||||||
):
|
|
||||||
product_update = products_factory.product_update_factory(
|
|
||||||
name='updated test product',
|
|
||||||
productor_id=productor.id,
|
|
||||||
)
|
|
||||||
product_id = 123
|
|
||||||
with pytest.raises(products_exceptions.ProductNotFoundError):
|
|
||||||
result = products_service.update_one(
|
|
||||||
session, product_id, product_update)
|
|
||||||
|
|
||||||
def test_update_product_invalidinput(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
productor: models.ProductorPublic,
|
|
||||||
products: list[models.ProductPublic]
|
|
||||||
):
|
|
||||||
product_id = products[0].id
|
|
||||||
product_update = products_factory.product_update_factory(
|
|
||||||
productor_id=123)
|
|
||||||
with pytest.raises(products_exceptions.ProductorNotFoundError):
|
|
||||||
result = products_service.update_one(
|
|
||||||
session, product_id, product_update)
|
|
||||||
|
|
||||||
product_update = products_factory.product_update_factory(
|
|
||||||
productor_id=productor.id,
|
|
||||||
referer_id=123
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_delete_product(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
products: list[models.ProductPublic]
|
|
||||||
):
|
|
||||||
product_id = products[0].id
|
|
||||||
result = products_service.delete_one(session, product_id)
|
|
||||||
|
|
||||||
check = products_service.get_one(session, product_id)
|
|
||||||
assert check is None
|
|
||||||
|
|
||||||
def test_delete_product_notfound(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
products: list[models.ProductPublic]
|
|
||||||
):
|
|
||||||
product_id = 123
|
|
||||||
with pytest.raises(products_exceptions.ProductNotFoundError):
|
|
||||||
result = products_service.delete_one(session, product_id)
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
import datetime
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
import src.shipments.exceptions as shipments_exceptions
|
|
||||||
import src.shipments.service as shipments_service
|
|
||||||
import tests.factories.shipments as shipments_factory
|
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
|
|
||||||
|
|
||||||
class TestShipmentsService:
|
|
||||||
def test_get_all_shipments(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
shipments: list[models.ShipmentPublic],
|
|
||||||
user: models.UserPublic,
|
|
||||||
):
|
|
||||||
result = shipments_service.get_all(session, user, [], [], [])
|
|
||||||
|
|
||||||
assert len(result) == 2
|
|
||||||
assert result == shipments
|
|
||||||
|
|
||||||
def test_get_all_shipments_filter_names(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
shipments: list[models.ShipmentPublic],
|
|
||||||
user: models.UserPublic,
|
|
||||||
):
|
|
||||||
result = shipments_service.get_all(
|
|
||||||
session, user, ['test shipment 1'], [], [])
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
assert result == [shipments[0]]
|
|
||||||
|
|
||||||
def test_get_all_shipments_filter_dates(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
shipments: list[models.ShipmentPublic],
|
|
||||||
user: models.UserPublic,
|
|
||||||
):
|
|
||||||
result = shipments_service.get_all(
|
|
||||||
session, user, [], ['2025-10-10'], [])
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
|
|
||||||
def test_get_all_shipments_filter_forms(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
shipments: list[models.ShipmentPublic],
|
|
||||||
forms: list[models.FormPublic],
|
|
||||||
user: models.UserPublic,
|
|
||||||
):
|
|
||||||
result = shipments_service.get_all(
|
|
||||||
session, user, [], [], [forms[0].name])
|
|
||||||
|
|
||||||
assert len(result) == 2
|
|
||||||
|
|
||||||
def test_get_all_shipments_all_filters(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
shipments: list[models.ShipmentPublic],
|
|
||||||
forms: list[models.FormPublic],
|
|
||||||
user: models.UserPublic,
|
|
||||||
):
|
|
||||||
result = shipments_service.get_all(session, user, ['test shipment 1'], [
|
|
||||||
'2025-10-10'], [forms[0].name])
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
|
|
||||||
def test_get_one_shipment(self, session: Session,
|
|
||||||
shipments: list[models.ShipmentPublic]):
|
|
||||||
result = shipments_service.get_one(session, shipments[0].id)
|
|
||||||
|
|
||||||
assert result == shipments[0]
|
|
||||||
|
|
||||||
def test_get_one_shipment_notfound(self, session: Session):
|
|
||||||
result = shipments_service.get_one(session, 122)
|
|
||||||
|
|
||||||
assert result is None
|
|
||||||
|
|
||||||
def test_create_shipment(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
):
|
|
||||||
shipment_create = shipments_factory.shipment_create_factory(
|
|
||||||
name='new test shipment',
|
|
||||||
date='2025-10-10',
|
|
||||||
)
|
|
||||||
result = shipments_service.create_one(session, shipment_create)
|
|
||||||
|
|
||||||
assert result.id is not None
|
|
||||||
assert result.name == "new test shipment"
|
|
||||||
|
|
||||||
def test_create_shipment_invalidinput(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
):
|
|
||||||
shipment_create = None
|
|
||||||
with pytest.raises(shipments_exceptions.ShipmentCreateError):
|
|
||||||
result = shipments_service.create_one(session, shipment_create)
|
|
||||||
|
|
||||||
def test_update_shipment(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
shipments: list[models.ShipmentPublic]
|
|
||||||
):
|
|
||||||
shipment_update = shipments_factory.shipment_update_factory(
|
|
||||||
name='updated shipment 1',
|
|
||||||
date='2025-12-10',
|
|
||||||
)
|
|
||||||
shipment_id = shipments[0].id
|
|
||||||
result = shipments_service.update_one(
|
|
||||||
session, shipment_id, shipment_update)
|
|
||||||
|
|
||||||
assert result.id == shipment_id
|
|
||||||
assert result.name == 'updated shipment 1'
|
|
||||||
assert result.date == datetime.date(2025, 12, 10)
|
|
||||||
|
|
||||||
def test_update_shipment_notfound(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
):
|
|
||||||
shipment_update = shipments_factory.shipment_update_factory(
|
|
||||||
name='updated shipment 1',
|
|
||||||
date=datetime.date(2025, 10, 10),
|
|
||||||
)
|
|
||||||
shipment_id = 123
|
|
||||||
with pytest.raises(shipments_exceptions.ShipmentNotFoundError):
|
|
||||||
result = shipments_service.update_one(
|
|
||||||
session, shipment_id, shipment_update)
|
|
||||||
|
|
||||||
def test_delete_shipment(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
shipments: list[models.ShipmentPublic]
|
|
||||||
):
|
|
||||||
shipment_id = shipments[0].id
|
|
||||||
result = shipments_service.delete_one(session, shipment_id)
|
|
||||||
|
|
||||||
check = shipments_service.get_one(session, shipment_id)
|
|
||||||
assert check is None
|
|
||||||
|
|
||||||
def test_delete_shipment_notfound(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
shipments: list[models.ShipmentPublic]
|
|
||||||
):
|
|
||||||
shipment_id = 123
|
|
||||||
with pytest.raises(shipments_exceptions.ShipmentNotFoundError):
|
|
||||||
result = shipments_service.delete_one(session, shipment_id)
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
import pytest
|
|
||||||
import src.users.exceptions as users_exceptions
|
|
||||||
import src.users.service as users_service
|
|
||||||
import tests.factories.users as users_factory
|
|
||||||
from sqlmodel import Session
|
|
||||||
from src import models
|
|
||||||
|
|
||||||
|
|
||||||
class TestUsersService:
|
|
||||||
def test_get_all_users(self, session: Session,
|
|
||||||
users: list[models.UserPublic]):
|
|
||||||
result = users_service.get_all(session, [], [])
|
|
||||||
|
|
||||||
assert len(result) == 3
|
|
||||||
assert result == users
|
|
||||||
|
|
||||||
def test_get_all_users_filter_names(
|
|
||||||
self, session: Session, users: list[models.UserPublic]):
|
|
||||||
result = users_service.get_all(session, ['test user 1 (admin)'], [])
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
assert result == [users[0]]
|
|
||||||
|
|
||||||
def test_get_all_users_filter_emails(
|
|
||||||
self, session: Session, users: list[models.UserPublic]):
|
|
||||||
result = users_service.get_all(session, [], ['test1@test.com'])
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
|
|
||||||
def test_get_all_users_all_filters(
|
|
||||||
self, session: Session, users: list[models.UserPublic]):
|
|
||||||
result = users_service.get_all(
|
|
||||||
session, ['test user 1 (admin)'], ['test1@test.com'])
|
|
||||||
|
|
||||||
assert len(result) == 1
|
|
||||||
|
|
||||||
def test_get_one_user(self, session: Session,
|
|
||||||
users: list[models.UserPublic]):
|
|
||||||
result = users_service.get_one(session, users[0].id)
|
|
||||||
|
|
||||||
assert result == users[0]
|
|
||||||
|
|
||||||
def test_get_one_user_notfound(self, session: Session):
|
|
||||||
result = users_service.get_one(session, 122)
|
|
||||||
|
|
||||||
assert result is None
|
|
||||||
|
|
||||||
def test_create_user(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
):
|
|
||||||
user_create = users_factory.user_create_factory(
|
|
||||||
name="new test user",
|
|
||||||
email='test@test.fr',
|
|
||||||
role_names=['test role']
|
|
||||||
)
|
|
||||||
result = users_service.create_one(session, user_create)
|
|
||||||
|
|
||||||
assert result.id is not None
|
|
||||||
assert result.name == "new test user"
|
|
||||||
assert result.email == "test@test.fr"
|
|
||||||
assert len(result.roles) == 1
|
|
||||||
|
|
||||||
def test_create_user_invalidinput(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
):
|
|
||||||
user_create = None
|
|
||||||
with pytest.raises(users_exceptions.UserCreateError):
|
|
||||||
result = users_service.create_one(session, user_create)
|
|
||||||
|
|
||||||
def test_update_user(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
users: list[models.UserPublic]
|
|
||||||
):
|
|
||||||
user_update = users_factory.user_update_factory(
|
|
||||||
name="updated test user",
|
|
||||||
email='test@testttt.fr',
|
|
||||||
role_names=['test role']
|
|
||||||
)
|
|
||||||
user_id = users[0].id
|
|
||||||
result = users_service.update_one(session, user_id, user_update)
|
|
||||||
|
|
||||||
assert result.id == user_id
|
|
||||||
assert result.name == 'updated test user'
|
|
||||||
assert result.email == 'test@testttt.fr'
|
|
||||||
|
|
||||||
def test_update_user_notfound(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
):
|
|
||||||
user_update = users_factory.user_update_factory(
|
|
||||||
name="updated test user",
|
|
||||||
email='test@testttt.fr',
|
|
||||||
role_names=['test role']
|
|
||||||
)
|
|
||||||
user_id = 123
|
|
||||||
with pytest.raises(users_exceptions.UserNotFoundError):
|
|
||||||
result = users_service.update_one(session, user_id, user_update)
|
|
||||||
|
|
||||||
def test_delete_user(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
users: list[models.UserPublic]
|
|
||||||
):
|
|
||||||
user_id = users[0].id
|
|
||||||
result = users_service.delete_one(session, user_id)
|
|
||||||
|
|
||||||
check = users_service.get_one(session, user_id)
|
|
||||||
assert check is None
|
|
||||||
|
|
||||||
def test_delete_user_notfound(
|
|
||||||
self,
|
|
||||||
session: Session,
|
|
||||||
users: list[models.UserPublic]
|
|
||||||
):
|
|
||||||
user_id = 123
|
|
||||||
with pytest.raises(users_exceptions.UserNotFoundError):
|
|
||||||
result = users_service.delete_one(session, user_id)
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
services:
|
|
||||||
frontend:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: frontend/Dockerfile.dev
|
|
||||||
volumes:
|
|
||||||
- ./frontend:/app
|
|
||||||
- /app/node_modules
|
|
||||||
environment:
|
|
||||||
VITE_API_URL: ${VITE_API_URL}
|
|
||||||
ports:
|
|
||||||
- "5173:5173"
|
|
||||||
depends_on:
|
|
||||||
- backend
|
|
||||||
backend:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: backend/Dockerfile
|
|
||||||
volumes:
|
|
||||||
- ./backend:/code
|
|
||||||
command: >
|
|
||||||
sh -c "fastapi run src/main.py --reload --port 8000"
|
|
||||||
environment:
|
|
||||||
ORIGINS: ${ORIGINS}
|
|
||||||
DB_HOST: database
|
|
||||||
DB_USER: ${DB_USER}
|
|
||||||
DB_PASS: ${DB_PASS}
|
|
||||||
DB_NAME: ${DB_NAME}
|
|
||||||
SECRET_KEY: ${SECRET_KEY}
|
|
||||||
VITE_API_URL: ${VITE_API_URL}
|
|
||||||
KEYCLOAK_SERVER: ${KEYCLOAK_SERVER}
|
|
||||||
KEYCLOAK_REALM: ${KEYCLOAK_REALM}
|
|
||||||
KEYCLOAK_CLIENT_ID: ${KEYCLOAK_CLIENT_ID}
|
|
||||||
KEYCLOAK_CLIENT_SECRET: ${KEYCLOAK_CLIENT_SECRET}
|
|
||||||
KEYCLOAK_REDIRECT_URI: ${KEYCLOAK_REDIRECT_URI}
|
|
||||||
DEBUG: true
|
|
||||||
MAX_AGE: ${MAX_AGE}
|
|
||||||
ports:
|
|
||||||
- "8000:8000"
|
|
||||||
depends_on:
|
|
||||||
- database
|
|
||||||
|
|
||||||
database:
|
|
||||||
image: postgres
|
|
||||||
restart: always
|
|
||||||
shm_size: 128mb
|
|
||||||
volumes:
|
|
||||||
- db:/var/lib/postgresql
|
|
||||||
environment:
|
|
||||||
POSTGRES_USER: ${DB_USER}
|
|
||||||
POSTGRES_PASSWORD: ${DB_PASS}
|
|
||||||
POSTGRES_DB: ${DB_NAME}
|
|
||||||
ports:
|
|
||||||
- 5432:5432
|
|
||||||
volumes:
|
|
||||||
db:
|
|
||||||
@@ -1,39 +1,32 @@
|
|||||||
|
version: "3.9"
|
||||||
services:
|
services:
|
||||||
nginx:
|
# nginx:
|
||||||
restart: always
|
# restart: always
|
||||||
build:
|
# build:
|
||||||
context: .
|
# context: .
|
||||||
dockerfile: frontend/Dockerfile
|
# dockerfile: front/Dockerfile
|
||||||
args:
|
# args:
|
||||||
VITE_API_URL: ${VITE_API_URL}
|
# VUE_APP_ROOT_FQDN: ${SERVICE_ROOT_FQDN}
|
||||||
ports:
|
# ports:
|
||||||
- 80:80
|
# - 80:80
|
||||||
depends_on:
|
# depends_on:
|
||||||
- backend
|
# - back
|
||||||
backend:
|
# back:
|
||||||
build:
|
# build:
|
||||||
context: .
|
# context: .
|
||||||
dockerfile: backend/Dockerfile
|
# dockerfile: back/Dockerfile
|
||||||
restart: always
|
# restart: always
|
||||||
environment:
|
# environment:
|
||||||
ORIGINS: ${ORIGINS}
|
# SERVICE_ORIGIN: ${SERVICE_ORIGIN}
|
||||||
DB_HOST: database
|
# DB_HOST: database
|
||||||
DB_USER: ${DB_USER}
|
# MARIADB_USER: ${MARIADB_USER}
|
||||||
DB_PASS: ${DB_PASS}
|
# MARIADB_PASSWORD: ${MARIADB_PASSWORD}
|
||||||
DB_NAME: ${DB_NAME}
|
# MARIADB_DATABASE: ${MARIADB_DATABASE}
|
||||||
SECRET_KEY: ${SECRET_KEY}
|
# SERVICE_SECRET_KEY: ${SERVICE_SECRET_KEY}
|
||||||
VITE_API_URL: ${VITE_API_URL}
|
# ports:
|
||||||
KEYCLOAK_SERVER: ${KEYCLOAK_SERVER}
|
# - 8000:8000
|
||||||
KEYCLOAK_REALM: ${KEYCLOAK_REALM}
|
# depends_on:
|
||||||
KEYCLOAK_CLIENT_ID: ${KEYCLOAK_CLIENT_ID}
|
# - database
|
||||||
KEYCLOAK_CLIENT_SECRET: ${KEYCLOAK_CLIENT_SECRET}
|
|
||||||
KEYCLOAK_REDIRECT_URI: ${KEYCLOAK_REDIRECT_URI}
|
|
||||||
DEBUG: ${DEBUG}
|
|
||||||
MAX_AGE: ${MAX_AGE}
|
|
||||||
ports:
|
|
||||||
- 8000:8000
|
|
||||||
depends_on:
|
|
||||||
- database
|
|
||||||
database:
|
database:
|
||||||
image: postgres
|
image: postgres
|
||||||
restart: always
|
restart: always
|
||||||
@@ -42,7 +35,8 @@ services:
|
|||||||
POSTGRES_USER: ${DB_USER}
|
POSTGRES_USER: ${DB_USER}
|
||||||
POSTGRES_PASSWORD: ${DB_PASS}
|
POSTGRES_PASSWORD: ${DB_PASS}
|
||||||
POSTGRES_DB: ${DB_NAME}
|
POSTGRES_DB: ${DB_NAME}
|
||||||
|
ROOT_FQDN: ${ROOT_FQDN}
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- "54321:5432"
|
||||||
volumes:
|
volumes:
|
||||||
db:
|
db:
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
FROM node:20.19-alpine AS build
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
ARG VITE_API_URL
|
|
||||||
ENV VITE_API_URL=$VITE_API_URL
|
|
||||||
|
|
||||||
COPY frontend/package.json frontend/package-lock.json /app/
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
COPY frontend/ .
|
|
||||||
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
FROM nginx:latest
|
|
||||||
|
|
||||||
COPY --from=build /app/dist /srv/www/frontend
|
|
||||||
|
|
||||||
RUN rm /etc/nginx/conf.d/default.conf
|
|
||||||
COPY --from=build /app/nginx/default.conf /etc/nginx/conf.d/default.conf
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
FROM node:20 AS dev
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY frontend/package.json ./
|
|
||||||
COPY frontend/package-lock.json ./
|
|
||||||
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
COPY frontend .
|
|
||||||
|
|
||||||
CMD ["npm", "run", "dev", "--", "--host"]
|
|
||||||
@@ -75,17 +75,12 @@
|
|||||||
"some contracts require a minimum value per shipment, ignore this field if it's not the case": "some contracts require a minimum value per shipment. Ignore this field if it does not apply to your contract.",
|
"some contracts require a minimum value per shipment, ignore this field if it's not the case": "some contracts require a minimum value per shipment. Ignore this field if it does not apply to your contract.",
|
||||||
"export contracts": "export contracts",
|
"export contracts": "export contracts",
|
||||||
"download recap": "download recap",
|
"download recap": "download recap",
|
||||||
"fill contract online": "fill contract online",
|
|
||||||
"download base template to print": "download base template to print",
|
|
||||||
"to export contracts submissions before sending to the productor go to the contracts section": "to export contracts submissions before sending to the productor go to the contracts section.",
|
"to export contracts submissions before sending to the productor go to the contracts section": "to export contracts submissions before sending to the productor go to the contracts section.",
|
||||||
"in this page you can view all contracts submissions, you can remove duplicates submission or download a specific contract": "in this page you can view all contracts submissions, you can remove duplicates submission or download a specific contract",
|
"in this page you can view all contracts submissions, you can remove duplicates submission or download a specific contract": "in this page you can view all contracts submissions, you can remove duplicates submission or download a specific contract",
|
||||||
"you can download all contracts for your form using the export all": "you can download all contracts for your form using the export all",
|
"you can download all contracts for your form using the export all": "you can download all contracts for your form using the export all",
|
||||||
"in the same corner you can download a recap by clicking on the button": "in the same corner you can download a recap by clicking on the",
|
"in the same corner you can download a recap by clicking on the button": "in the same corner you can download a recap by clicking on the",
|
||||||
"once all contracts downloaded, you can delete the form (to avoid new submissions) and hide it from the home page": "once all contracts downloaded, you can delete the form (to avoid new submissions) and hide it from the home page",
|
"once all contracts downloaded, you can delete the form (to avoid new submissions) and hide it from the home page": "once all contracts downloaded, you can delete the form (to avoid new submissions) and hide it from the home page",
|
||||||
"by checking this option the form will be accessible publicly on the home page, only check it if everything is fine with your form": "by checking this option the form will be accessible publicly on the home page, only check it if everything is fine with your form",
|
|
||||||
"contracts": "contracts",
|
"contracts": "contracts",
|
||||||
"hidden": "hidden",
|
|
||||||
"visible": "visible",
|
|
||||||
"minimum price for this shipment should be at least": "minimum price for this shipment should be at least",
|
"minimum price for this shipment should be at least": "minimum price for this shipment should be at least",
|
||||||
"there is": "there is",
|
"there is": "there is",
|
||||||
"for this contract": "for this contract.",
|
"for this contract": "for this contract.",
|
||||||
@@ -95,8 +90,6 @@
|
|||||||
"templates": "templates",
|
"templates": "templates",
|
||||||
"users": "users",
|
"users": "users",
|
||||||
"forms": "contract forms",
|
"forms": "contract forms",
|
||||||
"max cheque number": "max cheque number",
|
|
||||||
"can be empty default to 3": "can be empty default to 3",
|
|
||||||
"form": "contract form",
|
"form": "contract form",
|
||||||
"select a form": "select a form",
|
"select a form": "select a form",
|
||||||
"download contracts": "download contracts",
|
"download contracts": "download contracts",
|
||||||
@@ -206,7 +199,6 @@
|
|||||||
"your session has expired please log in again": "your session has expired please log in again",
|
"your session has expired please log in again": "your session has expired please log in again",
|
||||||
"session expired": "session expired",
|
"session expired": "session expired",
|
||||||
"user not allowed": "user not allowed",
|
"user not allowed": "user not allowed",
|
||||||
"roles": "roles",
|
|
||||||
"your keycloak user has no roles, please contact your administrator": "your keycloak user has no roles, please contact your administrator",
|
"your keycloak user has no roles, please contact your administrator": "your keycloak user has no roles, please contact your administrator",
|
||||||
"choose payment method": "choose your payment method (you do not need to pay now).",
|
"choose payment method": "choose your payment method (you do not need to pay now).",
|
||||||
"the product unit will be assigned to the quantity requested in the form": "the product unit defines the unit used in the contract form.",
|
"the product unit will be assigned to the quantity requested in the form": "the product unit defines the unit used in the contract form.",
|
||||||
|
|||||||
@@ -73,10 +73,7 @@
|
|||||||
"shipment products is necessary only for occasional products (if all products are recurrent leave empty)": "il est nécessaire de configurer les produits pour la livraison uniquement si il y a des produits occasionnels (laisser vide si tous les produits sont récurents).",
|
"shipment products is necessary only for occasional products (if all products are recurrent leave empty)": "il est nécessaire de configurer les produits pour la livraison uniquement si il y a des produits occasionnels (laisser vide si tous les produits sont récurents).",
|
||||||
"recurrent product is for all shipments, occasional product is for a specific shipment (see shipment form)": "les produits récurrents sont pour toutes les livraisons, les produits occasionnels sont pour une livraison particulière (voir formulaire de création de livraison).",
|
"recurrent product is for all shipments, occasional product is for a specific shipment (see shipment form)": "les produits récurrents sont pour toutes les livraisons, les produits occasionnels sont pour une livraison particulière (voir formulaire de création de livraison).",
|
||||||
"some contracts require a minimum value per shipment, ignore this field if it's not the case": "certains contrats nécessitent une valeur minimum par livraison. Ce champ peut être ignoré s’il ne s’applique pas à votre contrat.",
|
"some contracts require a minimum value per shipment, ignore this field if it's not the case": "certains contrats nécessitent une valeur minimum par livraison. Ce champ peut être ignoré s’il ne s’applique pas à votre contrat.",
|
||||||
"by checking this option the form will be accessible publicly on the home page, only check it if everything is fine with your form": "en cochant cette option le formulaire sera accessible publiquement sur la page d'accueil, cochez cette option uniquement si tout est prêt avec votre formulaire.",
|
|
||||||
"contracts": "contrats",
|
"contracts": "contrats",
|
||||||
"hidden": "caché",
|
|
||||||
"visible": "visible",
|
|
||||||
"minimum price for this shipment should be at least": "le prix minimum d'une livraison doit être au moins de",
|
"minimum price for this shipment should be at least": "le prix minimum d'une livraison doit être au moins de",
|
||||||
"there is": "il y a",
|
"there is": "il y a",
|
||||||
"for this contract": "pour ce contrat.",
|
"for this contract": "pour ce contrat.",
|
||||||
@@ -92,13 +89,9 @@
|
|||||||
"all contracts": "tous les contrats",
|
"all contracts": "tous les contrats",
|
||||||
"remove contract": "supprimer le contrat",
|
"remove contract": "supprimer le contrat",
|
||||||
"download contract": "télécharger le contrat",
|
"download contract": "télécharger le contrat",
|
||||||
"fill contract online": "remplir le contrat en ligne",
|
|
||||||
"download base template to print": "télécharger le contrat à remplir sur papier",
|
|
||||||
"by selecting a form here you can download all contracts of your form": "en selectionnant un formulaire, vous téléchargez tous les contrats pour un formulaire donné.",
|
"by selecting a form here you can download all contracts of your form": "en selectionnant un formulaire, vous téléchargez tous les contrats pour un formulaire donné.",
|
||||||
"edit user": "modifier l'utilisateur·trice",
|
"edit user": "modifier l'utilisateur·trice",
|
||||||
"remove user": "supprimer l'utilisateur·trice",
|
"remove user": "supprimer l'utilisateur·trice",
|
||||||
"max cheque number": "numbre maximum de cheques possible",
|
|
||||||
"can be empty default to 3": "optionnel, la valeur par défaut est à 3 cheques",
|
|
||||||
"all forms": "tous les formulaires de contrat",
|
"all forms": "tous les formulaires de contrat",
|
||||||
"create new form": "créer un nouveau formulaire de contrat",
|
"create new form": "créer un nouveau formulaire de contrat",
|
||||||
"actions": "actions",
|
"actions": "actions",
|
||||||
@@ -169,7 +162,7 @@
|
|||||||
"with cheque and transfer": "avec chèques et virements configuré pour le producteur",
|
"with cheque and transfer": "avec chèques et virements configuré pour le producteur",
|
||||||
"mililiter": "mililitres (ml)",
|
"mililiter": "mililitres (ml)",
|
||||||
"this field is optionnal a product can have a quantity if configured inside the product it will be shown inside the form": "ce champ est optionnel dans la configuration d'un produit, il représente la quantité d'un produit (poids d'une tranche de foie, poids d'un panier, taille d'un bocal...). Si ce champs est renseigné il sera affiché dans le formulaire à destination des amapiens.",
|
"this field is optionnal a product can have a quantity if configured inside the product it will be shown inside the form": "ce champ est optionnel dans la configuration d'un produit, il représente la quantité d'un produit (poids d'une tranche de foie, poids d'un panier, taille d'un bocal...). Si ce champs est renseigné il sera affiché dans le formulaire à destination des amapiens.",
|
||||||
"this field is also optionnal if a product have a quantity you can select the correct unit (metric system). It will be shown next to product quantity inside the form": "ce champs est optionnel dans la configuation d'un produit, il représente l'unité de mesure associée à la quantité d'un produit (g, kg, ml, L). Si ce champs est renseigné il sera affiché dans le formulaire à destination des amapiens à coté de la quantité du produit.",
|
"this field is also optionnal if a product have a quantity you can select the correct unit (metric system). It will be shown next to product quantity inside the form": "ce champs est optionnel dans la configuation d'un produit, il représente l'unité de mesure associé à la quantité d'un produit (g, kg, ml, L). Si ce champs est renseigné il sera affiché dans le formulaire à destination des amapiens à coté de la quantité du produit.",
|
||||||
"with 150 set as quantity and g as quantity unit in product": "avec \"150\" en quantité de produit et \"grammes\" selectionné dans l'unité de quantité du produit",
|
"with 150 set as quantity and g as quantity unit in product": "avec \"150\" en quantité de produit et \"grammes\" selectionné dans l'unité de quantité du produit",
|
||||||
"all shipments should be recreated for each form creation": "les livraisons étant liées à un formulaire elles doivent être recréés pour chaque nouveau formulaire.",
|
"all shipments should be recreated for each form creation": "les livraisons étant liées à un formulaire elles doivent être recréés pour chaque nouveau formulaire.",
|
||||||
"a productor can be edited if its informations change, it should not be recreated for each contracts": "un(e) producteur·trice peut être édité si ses informations changent, il/elle ne doit pas être recréé pour chaque nouveau contrat.",
|
"a productor can be edited if its informations change, it should not be recreated for each contracts": "un(e) producteur·trice peut être édité si ses informations changent, il/elle ne doit pas être recréé pour chaque nouveau contrat.",
|
||||||
@@ -206,7 +199,6 @@
|
|||||||
"your session has expired please log in again": "votre session a expiré veuillez vous reconnecter.",
|
"your session has expired please log in again": "votre session a expiré veuillez vous reconnecter.",
|
||||||
"session expired": "session expirée",
|
"session expired": "session expirée",
|
||||||
"user not allowed": "utilisateur non authorisé",
|
"user not allowed": "utilisateur non authorisé",
|
||||||
"roles": "roles",
|
|
||||||
"your keycloak user has no roles, please contact your administrator": "votre utilisateur keycloak n'a pas de roles configurés, contactez votre administrateur.",
|
"your keycloak user has no roles, please contact your administrator": "votre utilisateur keycloak n'a pas de roles configurés, contactez votre administrateur.",
|
||||||
"choose payment method": "choisissez votre méthode de paiement (vous n'avez pas à payer tout de suite, uniquement renseigner comment vous souhaitez régler votre commande).",
|
"choose payment method": "choisissez votre méthode de paiement (vous n'avez pas à payer tout de suite, uniquement renseigner comment vous souhaitez régler votre commande).",
|
||||||
"the product unit will be assigned to the quantity requested in the form": "l'unité de vente du produit définit l'unité associée à la quantité demandée dans le formulaire des amapiens.",
|
"the product unit will be assigned to the quantity requested in the form": "l'unité de vente du produit définit l'unité associée à la quantité demandée dans le formulaire des amapiens.",
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80 default_server;
|
|
||||||
listen [::]:80 default_server;
|
|
||||||
|
|
||||||
server_name localhost;
|
|
||||||
root /srv/www/frontend;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
location /api/ {
|
|
||||||
proxy_set_header Host $http_host;
|
|
||||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
||||||
proxy_set_header X-Forwarded-Proto $scheme;
|
|
||||||
|
|
||||||
proxy_redirect off;
|
|
||||||
proxy_buffering off;
|
|
||||||
proxy_pass http://backend:8000;
|
|
||||||
}
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri /index.html;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user