Compare commits
13 Commits
2a45d45bc1
...
bac2a7a780
| Author | SHA1 | Date | |
|---|---|---|---|
| bac2a7a780 | |||
| 19dbc49dc0 | |||
| 873f0d84b3 | |||
| 59f9170e9f | |||
| 230090ae22 | |||
| 840d5f5f13 | |||
| ab21b0e496 | |||
|
|
b27ee53a9e | ||
|
|
cc766238f2 | ||
| 63ae001a23 | |||
|
|
04ee7b7fb2 | ||
|
|
c6502e59a7 | ||
| c5c7963b5a |
@ -3244,7 +3244,7 @@ BEGIN
|
|||||||
LEFT JOIN collegial_approval ca ON ca.line_id = bl.id AND s_ca
|
LEFT JOIN collegial_approval ca ON ca.line_id = bl.id AND s_ca
|
||||||
LEFT JOIN ckk ck ON ck.line_id = bl.id AND (s_ckk OR s_need_bk)
|
LEFT JOIN ckk ck ON ck.line_id = bl.id AND (s_ckk OR s_need_bk)
|
||||||
LEFT JOIN contract_detail cd ON cd.line_id = bl.id AND (s_cd OR s_need_bk)
|
LEFT JOIN contract_detail cd ON cd.line_id = bl.id AND (s_cd OR s_need_bk)
|
||||||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet
|
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||||||
AND (s_need_ap OR s_cs OR s_ca OR s_ckk OR s_cd OR s_need_bk)
|
AND (s_need_ap OR s_cs OR s_ca OR s_ckk OR s_cd OR s_need_bk)
|
||||||
GROUP BY bl.expense_item_id
|
GROUP BY bl.expense_item_id
|
||||||
),
|
),
|
||||||
@ -3257,7 +3257,7 @@ BEGIN
|
|||||||
SUM(blq.transfer_to_q2) AS tq2, SUM(blq.transfer_to_q3) AS tq3, SUM(blq.transfer_to_q4) AS tq4
|
SUM(blq.transfer_to_q2) AS tq2, SUM(blq.transfer_to_q3) AS tq3, SUM(blq.transfer_to_q4) AS tq4
|
||||||
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||||||
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 1
|
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 1
|
||||||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_q1
|
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_q1 AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||||||
GROUP BY bl.expense_item_id
|
GROUP BY bl.expense_item_id
|
||||||
),
|
),
|
||||||
aq2 AS (
|
aq2 AS (
|
||||||
@ -3270,7 +3270,7 @@ BEGIN
|
|||||||
SUM(blq.transfer_to_q3) AS tq3, SUM(blq.transfer_to_q4) AS tq4
|
SUM(blq.transfer_to_q3) AS tq3, SUM(blq.transfer_to_q4) AS tq4
|
||||||
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||||||
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 2
|
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 2
|
||||||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_q2
|
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_q2 AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||||||
GROUP BY bl.expense_item_id
|
GROUP BY bl.expense_item_id
|
||||||
),
|
),
|
||||||
aq3 AS (
|
aq3 AS (
|
||||||
@ -3283,7 +3283,7 @@ BEGIN
|
|||||||
SUM(blq.transfer_to_q4) AS tq4
|
SUM(blq.transfer_to_q4) AS tq4
|
||||||
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||||||
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 3
|
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 3
|
||||||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_q3
|
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_q3 AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||||||
GROUP BY bl.expense_item_id
|
GROUP BY bl.expense_item_id
|
||||||
),
|
),
|
||||||
aq4 AS (
|
aq4 AS (
|
||||||
@ -3296,7 +3296,7 @@ BEGIN
|
|||||||
SUM(blq.actual_spod) AS spod
|
SUM(blq.actual_spod) AS spod
|
||||||
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||||||
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 4
|
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id AND blq.quarter = 4
|
||||||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND (s_q4 OR s_tot)
|
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND (s_q4 OR s_tot) AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||||||
GROUP BY bl.expense_item_id
|
GROUP BY bl.expense_item_id
|
||||||
),
|
),
|
||||||
atot AS (
|
atot AS (
|
||||||
@ -3308,7 +3308,7 @@ BEGIN
|
|||||||
SUM(blq.payment_amount) AS pay
|
SUM(blq.payment_amount) AS pay
|
||||||
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
FROM budget_line bl JOIN expense_item ei ON ei.id = bl.expense_item_id
|
||||||
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id
|
LEFT JOIN budget_line_quarter blq ON blq.line_id = bl.id
|
||||||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_tot
|
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet AND s_tot AND (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||||||
GROUP BY bl.expense_item_id
|
GROUP BY bl.expense_item_id
|
||||||
),
|
),
|
||||||
-- ═══ Pre-computed per-node aggregates (one pass over tw × agg) ═════════
|
-- ═══ Pre-computed per-node aggregates (one pass over tw × agg) ═════════
|
||||||
@ -4427,7 +4427,7 @@ BEGIN
|
|||||||
LEFT JOIN v3.sequestration sg ON sg.line_id = bl.id AND sg.actor='SSP_GO' AND s_need_ap
|
LEFT JOIN v3.sequestration sg ON sg.line_id = bl.id AND sg.actor='SSP_GO' AND s_need_ap
|
||||||
LEFT JOIN v3.reserve r ON r.line_id = bl.id AND s_need_ap
|
LEFT JOIN v3.reserve r ON r.line_id = bl.id AND s_need_ap
|
||||||
LEFT JOIN v3.ckk ck ON ck.line_id = bl.id AND s_book
|
LEFT JOIN v3.ckk ck ON ck.line_id = bl.id AND s_book
|
||||||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet
|
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet and (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||||||
AND (s_need_ap OR s_book)
|
AND (s_need_ap OR s_book)
|
||||||
GROUP BY bl.expense_item_id
|
GROUP BY bl.expense_item_id
|
||||||
),
|
),
|
||||||
@ -4453,7 +4453,7 @@ BEGIN
|
|||||||
FROM v3.budget_line bl
|
FROM v3.budget_line bl
|
||||||
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
||||||
JOIN v3.budget_line_quarter q ON q.line_id = bl.id
|
JOIN v3.budget_line_quarter q ON q.line_id = bl.id
|
||||||
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet
|
WHERE bl.budget_form_id = p_form_id AND ei.sheet = p_sheet and (p_direction is null OR bl.direction is null or bl.direction = p_direction)
|
||||||
GROUP BY bl.expense_item_id, q.quarter
|
GROUP BY bl.expense_item_id, q.quarter
|
||||||
),
|
),
|
||||||
-- Tree-rollup
|
-- Tree-rollup
|
||||||
|
|||||||
@ -6,46 +6,46 @@ from src.db.session import get_db
|
|||||||
from src.domain.schemas import LoginRequest, RefreshRequest, Token
|
from src.domain.schemas import LoginRequest, RefreshRequest, Token
|
||||||
from src.services.auth_service import AuthService
|
from src.services.auth_service import AuthService
|
||||||
|
|
||||||
if not settings.DEBUG:
|
# if not settings.DEBUG:
|
||||||
from raisa_fastapi_protected_api import UserInfo, get_user_dependency
|
# from raisa_fastapi_protected_api import UserInfo, get_user_dependency
|
||||||
|
|
||||||
router = APIRouter(prefix="/auth", tags=["auth"])
|
router = APIRouter(prefix="/auth", tags=["auth"])
|
||||||
|
|
||||||
|
|
||||||
if settings.DEBUG:
|
# if settings.DEBUG:
|
||||||
|
|
||||||
@router.post("/login", response_model=Token)
|
@router.post("/login", response_model=Token)
|
||||||
async def login(
|
async def login(
|
||||||
login_data: LoginRequest,
|
login_data: LoginRequest,
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
):
|
):
|
||||||
auth_service = AuthService(db)
|
auth_service = AuthService(db)
|
||||||
token = await auth_service.authenticate_user(login_data.username, login_data.password)
|
token = await auth_service.authenticate_user(login_data.username, login_data.password)
|
||||||
if not token:
|
if not token:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Неверное имя пользователя или пароль",
|
detail="Неверное имя пользователя или пароль",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
return token
|
return token
|
||||||
|
|
||||||
else:
|
# else:
|
||||||
|
|
||||||
@router.post("/login", response_model=Token)
|
# @router.post("/login", response_model=Token)
|
||||||
async def login(
|
# async def login(
|
||||||
login_data: LoginRequest,
|
# login_data: LoginRequest,
|
||||||
db: AsyncSession = Depends(get_db),
|
# db: AsyncSession = Depends(get_db),
|
||||||
user: UserInfo = Depends(get_user_dependency),
|
# user: UserInfo = Depends(get_user_dependency),
|
||||||
):
|
# ):
|
||||||
auth_service = AuthService(db)
|
# auth_service = AuthService(db)
|
||||||
token = await auth_service.authenticate_user_via_email(user.email)
|
# token = await auth_service.authenticate_user_via_email(user.email)
|
||||||
if not token:
|
# if not token:
|
||||||
raise HTTPException(
|
# raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
# status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Неверное имя пользователя или пароль",
|
# detail="Неверное имя пользователя или пароль",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
# headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
# )
|
||||||
return token
|
# return token
|
||||||
|
|
||||||
|
|
||||||
@router.post("/login-form", response_model=Token)
|
@router.post("/login-form", response_model=Token)
|
||||||
|
|||||||
@ -12,62 +12,62 @@ from src.repository.user_repository import UserRepository
|
|||||||
security = HTTPBearer()
|
security = HTTPBearer()
|
||||||
|
|
||||||
|
|
||||||
if settings.DEBUG:
|
# if settings.DEBUG:
|
||||||
|
|
||||||
async def get_user_by_token(
|
async def get_user_by_token(
|
||||||
token: str,
|
token: str,
|
||||||
db: AsyncSession,
|
db: AsyncSession,
|
||||||
) -> AppUser:
|
) -> AppUser:
|
||||||
payload = verify_token(token)
|
payload = verify_token(token)
|
||||||
if payload is None:
|
if payload is None:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Недействительный токен",
|
detail="Недействительный токен",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
|
|
||||||
username: str | None = payload.get("sub")
|
username: str | None = payload.get("sub")
|
||||||
if username is None:
|
if username is None:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Недействительный токен",
|
detail="Недействительный токен",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
|
|
||||||
user_repo = UserRepository(db)
|
user_repo = UserRepository(db)
|
||||||
user = await user_repo.get_by_username(username)
|
user = await user_repo.get_by_username(username)
|
||||||
if user is None:
|
if user is None:
|
||||||
user = await user_repo.get_by_email(username)
|
user = await user_repo.get_by_email(username)
|
||||||
if user is None:
|
if user is None:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Пользователь не найден",
|
detail="Пользователь не найден",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
async def get_current_user(
|
async def get_current_user(
|
||||||
credentials: HTTPAuthorizationCredentials = Depends(security),
|
credentials: HTTPAuthorizationCredentials = Depends(security),
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
) -> AppUser:
|
) -> AppUser:
|
||||||
return await get_user_by_token(token=credentials.credentials, db=db)
|
return await get_user_by_token(token=credentials.credentials, db=db)
|
||||||
|
|
||||||
else:
|
# else:
|
||||||
from raisa_fastapi_protected_api import UserInfo, get_user_dependency
|
# from raisa_fastapi_protected_api import UserInfo, get_user_dependency
|
||||||
|
|
||||||
async def get_current_user(
|
# async def get_current_user(
|
||||||
user: UserInfo = Depends(get_user_dependency),
|
# user: UserInfo = Depends(get_user_dependency),
|
||||||
db: AsyncSession = Depends(get_db),
|
# db: AsyncSession = Depends(get_db),
|
||||||
) -> AppUser:
|
# ) -> AppUser:
|
||||||
user_repo = UserRepository(db)
|
# user_repo = UserRepository(db)
|
||||||
db_user = await user_repo.get_by_email(user.email)
|
# db_user = await user_repo.get_by_email(user.email)
|
||||||
if db_user is None:
|
# if db_user is None:
|
||||||
raise HTTPException(
|
# raise HTTPException(
|
||||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
# status_code=status.HTTP_401_UNAUTHORIZED,
|
||||||
detail="Пользователь не найден",
|
# detail="Пользователь не найден",
|
||||||
headers={"WWW-Authenticate": "Bearer"},
|
# headers={"WWW-Authenticate": "Bearer"},
|
||||||
)
|
# )
|
||||||
return db_user
|
# return db_user
|
||||||
|
|
||||||
|
|
||||||
async def get_current_active_user(current_user: AppUser = Depends(get_current_user)) -> AppUser:
|
async def get_current_active_user(current_user: AppUser = Depends(get_current_user)) -> AppUser:
|
||||||
|
|||||||
@ -59,12 +59,13 @@ async def get_vsp(
|
|||||||
@router.get("/dropdown", response_model=BaseListResponse[VSPInDB])
|
@router.get("/dropdown", response_model=BaseListResponse[VSPInDB])
|
||||||
async def get_vsp_dropdown(
|
async def get_vsp_dropdown(
|
||||||
ssp_id: int | None = None,
|
ssp_id: int | None = None,
|
||||||
|
form_id: int | None = None,
|
||||||
db: AsyncSession = Depends(get_db),
|
db: AsyncSession = Depends(get_db),
|
||||||
current_user: AppUser = Depends(require_executor),
|
current_user: AppUser = Depends(require_executor),
|
||||||
):
|
):
|
||||||
"""Получение записей для выпадающего списка INFO."""
|
"""Получение записей для выпадающего списка INFO."""
|
||||||
service = VSPService(db)
|
service = VSPService(db)
|
||||||
result = await service.get_dropdown(user=current_user, ssp_id=ssp_id, with_count=True)
|
result = await service.get_dropdown(user=current_user, ssp_id=ssp_id, form_id=form_id, with_count=True)
|
||||||
return BaseListResponse(
|
return BaseListResponse(
|
||||||
success=True,
|
success=True,
|
||||||
message="Список INFO для выпадающего списка",
|
message="Список INFO для выпадающего списка",
|
||||||
|
|||||||
104
api/src/main.py
104
api/src/main.py
@ -22,18 +22,18 @@ from src.db.base import engine
|
|||||||
from src.db.session import create_tables
|
from src.db.session import create_tables
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
|
# if not settings.DEBUG:
|
||||||
if not settings.DEBUG:
|
# from raisa_fastapi_protected_api import (
|
||||||
from raisa_fastapi_protected_api import (
|
# AuthorizationMiddleware,
|
||||||
AuthorizationMiddleware,
|
# OpenEndpoint,
|
||||||
OpenEndpoint,
|
# ProtectedOAuthSettings,
|
||||||
ProtectedOAuthSettings,
|
# ProtectedRolesSettings,
|
||||||
ProtectedRolesSettings,
|
# ProtectedSettings,
|
||||||
ProtectedSettings,
|
# SearchType,
|
||||||
SearchType,
|
# )
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
_AUDIT_CLEANUP_LOCK_KEY = 21987431
|
_AUDIT_CLEANUP_LOCK_KEY = 21987431
|
||||||
@ -104,13 +104,13 @@ async def lifespan(app: FastAPI):
|
|||||||
cleanup_task: asyncio.Task | None = None
|
cleanup_task: asyncio.Task | None = None
|
||||||
if not settings.DEBUG:
|
if not settings.DEBUG:
|
||||||
await create_tables()
|
await create_tables()
|
||||||
ProtectedSettings(
|
# ProtectedSettings(
|
||||||
ProtectedOAuthSettings(JWKS_URI=settings.JWKS_URL),
|
# ProtectedOAuthSettings(JWKS_URI=settings.JWKS_URL),
|
||||||
ProtectedRolesSettings(
|
# ProtectedRolesSettings(
|
||||||
APP_NAMESPACE=settings.APP_NAMESPACE,
|
# APP_NAMESPACE=settings.APP_NAMESPACE,
|
||||||
APP_NAME=settings.APP_NAME,
|
# APP_NAME=settings.APP_NAME,
|
||||||
),
|
# ),
|
||||||
)
|
# )
|
||||||
cleanup_task = asyncio.create_task(_audit_cleanup_loop())
|
cleanup_task = asyncio.create_task(_audit_cleanup_loop())
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
@ -185,41 +185,41 @@ async def readyz(response: Response):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
if not settings.DEBUG:
|
# if not settings.DEBUG:
|
||||||
app.add_middleware(
|
# app.add_middleware(
|
||||||
AuthorizationMiddleware,
|
# AuthorizationMiddleware,
|
||||||
open_endpoints=[
|
# open_endpoints=[
|
||||||
OpenEndpoint(path="", type_search=SearchType.ABSOLUTE),
|
# OpenEndpoint(path="", type_search=SearchType.ABSOLUTE),
|
||||||
OpenEndpoint(path=settings.ROOT_PATH + "", type_search=SearchType.ABSOLUTE),
|
# OpenEndpoint(path=settings.ROOT_PATH + "", type_search=SearchType.ABSOLUTE),
|
||||||
OpenEndpoint(path="/", type_search=SearchType.ABSOLUTE),
|
# OpenEndpoint(path="/", type_search=SearchType.ABSOLUTE),
|
||||||
OpenEndpoint(path=settings.ROOT_PATH + "/", type_search=SearchType.ABSOLUTE),
|
# OpenEndpoint(path=settings.ROOT_PATH + "/", type_search=SearchType.ABSOLUTE),
|
||||||
OpenEndpoint(path="/healthcheck", type_search=SearchType.ABSOLUTE),
|
# OpenEndpoint(path="/healthcheck", type_search=SearchType.ABSOLUTE),
|
||||||
OpenEndpoint(
|
# OpenEndpoint(
|
||||||
path=settings.ROOT_PATH + "/healthcheck",
|
# path=settings.ROOT_PATH + "/healthcheck",
|
||||||
type_search=SearchType.ABSOLUTE,
|
# type_search=SearchType.ABSOLUTE,
|
||||||
),
|
# ),
|
||||||
OpenEndpoint(path="/healthz", type_search=SearchType.ABSOLUTE),
|
# OpenEndpoint(path="/healthz", type_search=SearchType.ABSOLUTE),
|
||||||
OpenEndpoint(
|
# OpenEndpoint(
|
||||||
path=settings.ROOT_PATH + "/healthz",
|
# path=settings.ROOT_PATH + "/healthz",
|
||||||
type_search=SearchType.ABSOLUTE,
|
# type_search=SearchType.ABSOLUTE,
|
||||||
),
|
# ),
|
||||||
OpenEndpoint(path="/readyz", type_search=SearchType.ABSOLUTE),
|
# OpenEndpoint(path="/readyz", type_search=SearchType.ABSOLUTE),
|
||||||
OpenEndpoint(
|
# OpenEndpoint(
|
||||||
path=settings.ROOT_PATH + "/readyz",
|
# path=settings.ROOT_PATH + "/readyz",
|
||||||
type_search=SearchType.ABSOLUTE,
|
# type_search=SearchType.ABSOLUTE,
|
||||||
),
|
# ),
|
||||||
OpenEndpoint(path="/openapi.json", type_search=SearchType.START),
|
# OpenEndpoint(path="/openapi.json", type_search=SearchType.START),
|
||||||
OpenEndpoint(
|
# OpenEndpoint(
|
||||||
path=settings.ROOT_PATH + "/openapi.json",
|
# path=settings.ROOT_PATH + "/openapi.json",
|
||||||
type_search=SearchType.START,
|
# type_search=SearchType.START,
|
||||||
),
|
# ),
|
||||||
OpenEndpoint(path="/docs", type_search=SearchType.START),
|
# OpenEndpoint(path="/docs", type_search=SearchType.START),
|
||||||
OpenEndpoint(
|
# OpenEndpoint(
|
||||||
path=settings.ROOT_PATH + "/docs",
|
# path=settings.ROOT_PATH + "/docs",
|
||||||
type_search=SearchType.START,
|
# type_search=SearchType.START,
|
||||||
),
|
# ),
|
||||||
],
|
# ],
|
||||||
)
|
# )
|
||||||
|
|
||||||
app.add_middleware(
|
app.add_middleware(
|
||||||
CORSMiddleware,
|
CORSMiddleware,
|
||||||
|
|||||||
@ -181,7 +181,7 @@ class DepthSheetWriter:
|
|||||||
worksheet.column_dimensions[col_letter].width = adjusted_width
|
worksheet.column_dimensions[col_letter].width = adjusted_width
|
||||||
|
|
||||||
def _set_color_to_row(self, ws: Worksheet, item_type: str) -> None:
|
def _set_color_to_row(self, ws: Worksheet, item_type: str) -> None:
|
||||||
if not self.palette:
|
if not self.palette or item_type in ("SUB_ITEM", "INPUT"):
|
||||||
return
|
return
|
||||||
|
|
||||||
row = ws.max_row
|
row = ws.max_row
|
||||||
|
|||||||
@ -7,6 +7,7 @@ from openpyxl import Workbook
|
|||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from src.repository.budget_form_repository import BudgetFormRepository
|
||||||
from src.core.errors import AccessDeniedException, ValidationException
|
from src.core.errors import AccessDeniedException, ValidationException
|
||||||
from src.db.models import AppUser, UserRoleEnum, Vsp
|
from src.db.models import AppUser, UserRoleEnum, Vsp
|
||||||
from src.domain.schemas import VSPCreate, VSPUpdate
|
from src.domain.schemas import VSPCreate, VSPUpdate
|
||||||
@ -29,6 +30,7 @@ class VSPService:
|
|||||||
def __init__(self, db: AsyncSession):
|
def __init__(self, db: AsyncSession):
|
||||||
self.db = db
|
self.db = db
|
||||||
self.vsp_repo = VSPRepository(db)
|
self.vsp_repo = VSPRepository(db)
|
||||||
|
self.form_repo = BudgetFormRepository(db)
|
||||||
self.user_repo = UserRepository(db)
|
self.user_repo = UserRepository(db)
|
||||||
|
|
||||||
async def get(self, vsp_id: int, user: AppUser, load_org_unit: bool = False) -> Optional[Vsp]:
|
async def get(self, vsp_id: int, user: AppUser, load_org_unit: bool = False) -> Optional[Vsp]:
|
||||||
@ -94,19 +96,27 @@ class VSPService:
|
|||||||
self,
|
self,
|
||||||
user: AppUser,
|
user: AppUser,
|
||||||
ssp_id: int | None = None,
|
ssp_id: int | None = None,
|
||||||
|
form_id: int | None = None,
|
||||||
with_count: bool = False,
|
with_count: bool = False,
|
||||||
) -> list[Vsp]:
|
) -> list[Vsp]:
|
||||||
ssp_ids = await self._get_scoped_ssp_ids(user=user)
|
ssp_ids = await self._get_scoped_ssp_ids(user=user)
|
||||||
|
|
||||||
|
checking_ssps = set()
|
||||||
if ssp_id is not None:
|
if ssp_id is not None:
|
||||||
|
checking_ssps.add(ssp_id)
|
||||||
|
if form_id is not None:
|
||||||
|
form = await self.form_repo.get(budget_form_id=form_id)
|
||||||
|
if form is not None:
|
||||||
|
checking_ssps.add(form.org_unit_id)
|
||||||
|
if checking_ssps:
|
||||||
if ssp_ids is None:
|
if ssp_ids is None:
|
||||||
ssp_ids = [ssp_id]
|
ssp_ids = list(checking_ssps)
|
||||||
elif ssp_id not in set(ssp_ids):
|
elif not checking_ssps & set(ssp_ids):
|
||||||
if with_count:
|
if with_count:
|
||||||
return 0, []
|
return 0, []
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
ssp_ids = [ssp_id]
|
ssp_ids = list(checking_ssps & set(ssp_ids))
|
||||||
|
|
||||||
return await self.vsp_repo.get_list(
|
return await self.vsp_repo.get_list(
|
||||||
ssp_ids=ssp_ids,
|
ssp_ids=ssp_ids,
|
||||||
|
|||||||
@ -37,14 +37,25 @@ def test_vsp_dropdown_smoke(client, admin_tokens, auth_headers):
|
|||||||
assert isinstance(payload["result"], list)
|
assert isinstance(payload["result"], list)
|
||||||
|
|
||||||
|
|
||||||
def test_vsp_dropdown_with_ssp_filter(client, admin_tokens, auth_headers):
|
@pytest.mark.parametrize(
|
||||||
|
"params, count",
|
||||||
|
[
|
||||||
|
([("ssp_id", 2)], 1),
|
||||||
|
([("form_id", 1)], 0),
|
||||||
|
([("form_id", 2)], 1),
|
||||||
|
([("ssp_id", 2), ("form_id", 1)], 1),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_vsp_dropdown_with_filter(client, admin_tokens, auth_headers, params, count):
|
||||||
|
filters = "&".join([f"{el[0]}={el[1]}" for el in params])
|
||||||
response = client.get(
|
response = client.get(
|
||||||
"/api/v1/dict/info/dropdown?ssp_id=1",
|
f"/api/v1/dict/info/dropdown?{filters}",
|
||||||
headers=auth_headers(admin_tokens),
|
headers=auth_headers(admin_tokens),
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
payload = response.json()
|
payload = response.json()
|
||||||
assert isinstance(payload["result"], list)
|
assert isinstance(payload["result"], list)
|
||||||
|
assert len(payload["result"]) == count
|
||||||
|
|
||||||
|
|
||||||
def test_vsp_get_by_id(client, admin_tokens, auth_headers):
|
def test_vsp_get_by_id(client, admin_tokens, auth_headers):
|
||||||
|
|||||||
@ -15,8 +15,9 @@ INSERT INTO v3.user_org (id,user_id,org_unit_id) VALUES
|
|||||||
SELECT setval('v3.user_org_id_seq', 3);
|
SELECT setval('v3.user_org_id_seq', 3);
|
||||||
|
|
||||||
INSERT INTO v3.budget_form (id,form_type_code,"year",created_by,created_at,updated_by,updated_at,org_unit_id) VALUES
|
INSERT INTO v3.budget_form (id,form_type_code,"year",created_by,created_at,updated_by,updated_at,org_unit_id) VALUES
|
||||||
(1,'FORM_1',2027,NULL,'2026-05-06 16:57:38.066371',NULL,'2026-06-11 14:35:53.783017',1);
|
(1,'FORM_1',2027,NULL,'2026-05-06 16:57:38.066371',NULL,'2026-06-11 14:35:53.783017',1),
|
||||||
SELECT setval('v3.budget_form_id_seq', 1);
|
(4,'FORM_2',2026,NULL,'2026-05-08 09:08:12.230445',NULL,'2026-05-08 09:08:12.230445',2);
|
||||||
|
SELECT setval('v3.budget_form_id_seq', 4);
|
||||||
|
|
||||||
INSERT INTO v3.budget_line (id,budget_form_id,expense_item_id,"name",internal_order,vsp_id,project_id,justification,created_by,created_at,updated_by,updated_at,direction) VALUES
|
INSERT INTO v3.budget_line (id,budget_form_id,expense_item_id,"name",internal_order,vsp_id,project_id,justification,created_by,created_at,updated_by,updated_at,direction) VALUES
|
||||||
(1,1,88,'1234',NULL,NULL,NULL,NULL,NULL,'2026-05-06 16:58:05.793083',NULL,'2026-06-22 17:41:54.255313','Support');
|
(1,1,88,'1234',NULL,NULL,NULL,NULL,NULL,'2026-05-06 16:58:05.793083',NULL,'2026-06-22 17:41:54.255313','Support');
|
||||||
@ -26,5 +27,6 @@ INSERT INTO v3.form_phase (budget_form_id,sheet,phase_code,"role",column_keys,op
|
|||||||
(1,'AHR','test','DFIP','{{plan.q1}}','2026-05-05 03:00:00+03','2026-06-06 03:00:00+03');
|
(1,'AHR','test','DFIP','{{plan.q1}}','2026-05-05 03:00:00+03','2026-06-06 03:00:00+03');
|
||||||
|
|
||||||
INSERT INTO v3.vsp (id,branch_id,reg_number,address,format,opened_at,placement_type,staff_count,total_area,closed_at,is_active,updated_at,is_deleted,created_at,created_by,system_code,updated_by,vsp_type,notes,location_form,numbers,rent_contract_num,rent_end_date) VALUES
|
INSERT INTO v3.vsp (id,branch_id,reg_number,address,format,opened_at,placement_type,staff_count,total_area,closed_at,is_active,updated_at,is_deleted,created_at,created_by,system_code,updated_by,vsp_type,notes,location_form,numbers,rent_contract_num,rent_end_date) VALUES
|
||||||
(1,2,'Тестовый всп 1','Тестовая 12','укукк','2026-05-07','ывс',2026,230,'2026-06-16',false,'2026-06-18 15:46:38.611903',false,'2026-06-04 14:36:59.737727',1,'1233',91,'','','встроенное помещение',NULL,'',NULL);
|
(1,2,'Тестовый всп 1','Тестовая 12','укукк','2026-05-07','ывс',2026,230,'2026-06-16',false,'2026-06-18 15:46:38.611903',false,'2026-06-04 14:36:59.737727',1,'1233',91,'','','встроенное помещение',NULL,'',NULL),
|
||||||
SELECT setval('v3.vsp_id_seq', 1);
|
(2,2,'Test12 -----','Тестовая 125','формат 15 ','2026-06-08',NULL,NULL,58,NULL,true,'2026-06-16 18:53:17.986651',false,'2026-06-09 09:04:33.29858',91,'123-1234',91,'субаренда','Работает ','аренда',155,'12-45','2026-10-02');
|
||||||
|
SELECT setval('v3.vsp_id_seq', 2);
|
||||||
|
|||||||
@ -143,7 +143,7 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
columnVirtualizerOptions: ({ table }) => ({
|
columnVirtualizerOptions: ({ table }) => ({
|
||||||
overscan: 1,
|
overscan: 10,
|
||||||
measureElement: (el) => {
|
measureElement: (el) => {
|
||||||
if (!el) return 150;
|
if (!el) return 150;
|
||||||
const index = Number(el?.getAttribute?.('data-index'));
|
const index = Number(el?.getAttribute?.('data-index'));
|
||||||
@ -152,7 +152,6 @@ const RealtimeTable = ({ formType, formId, sheetName, direction }) => {
|
|||||||
const colId = table.getState().columnPinning.left[index];
|
const colId = table.getState().columnPinning.left[index];
|
||||||
const column = table.getColumn(colId);
|
const column = table.getColumn(colId);
|
||||||
return column.columnDef.size;
|
return column.columnDef.size;
|
||||||
|
|
||||||
}
|
}
|
||||||
const allCols = [...table.getLeftVisibleLeafColumns(), ...table.getCenterVisibleLeafColumns()]
|
const allCols = [...table.getLeftVisibleLeafColumns(), ...table.getCenterVisibleLeafColumns()]
|
||||||
return allCols[index]?.getSize() ?? 150;
|
return allCols[index]?.getSize() ?? 150;
|
||||||
|
|||||||
@ -168,7 +168,7 @@ const SettingPanel = ({
|
|||||||
<ExportDefaultButton
|
<ExportDefaultButton
|
||||||
onClick={handleExport}
|
onClick={handleExport}
|
||||||
disabled={!formId || !sheetName || isExporting}
|
disabled={!formId || !sheetName || isExporting}
|
||||||
text={isExporting ? 'Экспорт...' : 'Экспорт в Excel'}
|
text={isExporting ? 'Экспорт...' : 'Экспорт'}
|
||||||
/>
|
/>
|
||||||
</GroupByObject>
|
</GroupByObject>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@ -38,13 +38,12 @@ export const AddStagesModal = ({
|
|||||||
sheetOptions = [], // [{ value: 'sheet1', label: 'Лист 1' }]
|
sheetOptions = [], // [{ value: 'sheet1', label: 'Лист 1' }]
|
||||||
formType = '',
|
formType = '',
|
||||||
defaultSheet = '',
|
defaultSheet = '',
|
||||||
defaultRole,
|
|
||||||
}) => {
|
}) => {
|
||||||
const [startDate, setStartDate] = useState(null);
|
const [startDate, setStartDate] = useState(null);
|
||||||
const [endDate, setEndDate] = useState(null);
|
const [endDate, setEndDate] = useState(null);
|
||||||
const [stageName, setStageName] = useState('');
|
const [stageName, setStageName] = useState('');
|
||||||
const [selectedSheet, setSelectedSheet] = useState(defaultSheet);
|
const [selectedSheet, setSelectedSheet] = useState(defaultSheet);
|
||||||
const [selectedRole, setSelectedRole] = useState(defaultRole);
|
const [selectedRole, setSelectedRole] = useState();
|
||||||
const [columnsConfig, setColumnsConfig] = useState({ columns: [], colors: {} });
|
const [columnsConfig, setColumnsConfig] = useState({ columns: [], colors: {} });
|
||||||
const [selectedColumns, setSelectedColumns] = useState([]);
|
const [selectedColumns, setSelectedColumns] = useState([]);
|
||||||
|
|
||||||
@ -89,10 +88,10 @@ export const AddStagesModal = ({
|
|||||||
setEndDate(null);
|
setEndDate(null);
|
||||||
setStageName('');
|
setStageName('');
|
||||||
setSelectedSheet(defaultSheet);
|
setSelectedSheet(defaultSheet);
|
||||||
setSelectedRole(defaultRole);
|
setSelectedRole();
|
||||||
setSelectedColumns([]);
|
setSelectedColumns([]);
|
||||||
}
|
}
|
||||||
}, [stage, isOpen, defaultSheet, defaultRole]);
|
}, [stage, isOpen, defaultSheet]);
|
||||||
|
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
const opensAt = formatDateForApi(startDate);
|
const opensAt = formatDateForApi(startDate);
|
||||||
@ -129,7 +128,7 @@ export const AddStagesModal = ({
|
|||||||
setEndDate(null);
|
setEndDate(null);
|
||||||
setStageName('');
|
setStageName('');
|
||||||
setSelectedSheet(defaultSheet);
|
setSelectedSheet(defaultSheet);
|
||||||
setSelectedRole(defaultRole);
|
setSelectedRole();
|
||||||
setSelectedColumns([]);
|
setSelectedColumns([]);
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
@ -238,6 +237,7 @@ export const AddStagesModal = ({
|
|||||||
placeholderSelect="Выбрано столбцов: "
|
placeholderSelect="Выбрано столбцов: "
|
||||||
selectedIds={selectedColumns}
|
selectedIds={selectedColumns}
|
||||||
onSelectNode={handleColumnsSelect}
|
onSelectNode={handleColumnsSelect}
|
||||||
|
forStage={true}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Stack>
|
</Stack>
|
||||||
@ -247,7 +247,7 @@ export const AddStagesModal = ({
|
|||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<FormLabel>Роль</FormLabel>
|
<FormLabel>Роль</FormLabel>
|
||||||
<Select
|
<Select
|
||||||
value={selectedRole}
|
value={selectedRole ?? ''}
|
||||||
onChange={(e) => setSelectedRole(e.target.value)}
|
onChange={(e) => setSelectedRole(e.target.value)}
|
||||||
displayEmpty
|
displayEmpty
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
@ -76,41 +76,59 @@ export const SettingModal = ({
|
|||||||
setIsAddEditModalOpen(true);
|
setIsAddEditModalOpen(true);
|
||||||
};
|
};
|
||||||
const transformToSheetOptions = (data) => {
|
const transformToSheetOptions = (data) => {
|
||||||
const sheetMap = new Map();
|
// TO DO пока не добавили direction для этапов
|
||||||
|
|
||||||
data.forEach(item => {
|
// const sheetMap = new Map();
|
||||||
const key = item.direction ? `${item.sheet}_${item.direction}` : item.sheet;
|
|
||||||
|
|
||||||
if (!sheetMap.has(key)) {
|
// data.forEach(item => {
|
||||||
sheetMap.set(key, {
|
// const key = item.direction ? `${item.sheet}_${item.direction}` : item.sheet;
|
||||||
sheet: item.sheet,
|
|
||||||
direction: item.direction,
|
|
||||||
names: []
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const entry = sheetMap.get(key);
|
// if (!sheetMap.has(key)) {
|
||||||
if (!entry.names.includes(item.name)) {
|
// sheetMap.set(key, {
|
||||||
entry.names.push(item.name);
|
// sheet: item.sheet,
|
||||||
}
|
// direction: item.direction,
|
||||||
});
|
// names: []
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const entry = sheetMap.get(key);
|
||||||
|
// if (!entry.names.includes(item.name)) {
|
||||||
|
// entry.names.push(item.name);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const sheetOptions = [];
|
||||||
|
|
||||||
|
// sheetMap.forEach((value) => {
|
||||||
|
// let label = value.names.join(' / ');
|
||||||
|
|
||||||
|
// if (value.direction) {
|
||||||
|
// label += ` (${value.direction})`;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// sheetOptions.push({
|
||||||
|
// value: value.direction ? `${value.sheet}_${value.direction}` : value.sheet,
|
||||||
|
// label: label,
|
||||||
|
// sheet: value.sheet,
|
||||||
|
// direction: value.direction
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return sheetOptions;
|
||||||
|
|
||||||
const sheetOptions = [];
|
const sheetOptions = [];
|
||||||
|
data.forEach((value) => {
|
||||||
sheetMap.forEach((value) => {
|
if (sheetOptions.find(s => s.sheet == value.sheet)) {
|
||||||
let label = value.names.join(' / ');
|
return;
|
||||||
|
|
||||||
if (value.direction) {
|
|
||||||
label += ` (${value.direction})`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sheetOptions.push({
|
sheetOptions.push({
|
||||||
value: value.direction ? `${value.sheet}_${value.direction}` : value.sheet,
|
value: value.sheet,
|
||||||
label: label,
|
label: value.name,
|
||||||
sheet: value.sheet,
|
sheet: value.sheet,
|
||||||
direction: value.direction
|
direction: value.direction,
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
return sheetOptions;
|
return sheetOptions;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export const ExportWithTextButton = (props) => {
|
|||||||
<Stack sx={{ flexDirection: 'row', alignItems: 'center', gap: '.375rem' }}>
|
<Stack sx={{ flexDirection: 'row', alignItems: 'center', gap: '.375rem' }}>
|
||||||
<ExportSvg size="1rem" color="#45881c" strokeWidth={'.33px'} />
|
<ExportSvg size="1rem" color="#45881c" strokeWidth={'.33px'} />
|
||||||
<span style={{ fontSize: '.88rem' }}>
|
<span style={{ fontSize: '.88rem' }}>
|
||||||
{props?.text || 'Экспорт в Excel'}
|
{props?.text || 'Экспорт'}
|
||||||
</span>
|
</span>
|
||||||
</Stack>
|
</Stack>
|
||||||
</PrimaryOutlinedButton>
|
</PrimaryOutlinedButton>
|
||||||
@ -57,7 +57,7 @@ export const ExportDefaultButton = (props) => {
|
|||||||
<DefaultOutlinedButton {...props}>
|
<DefaultOutlinedButton {...props}>
|
||||||
<Stack sx={{ flexDirection: 'row', alignItems: 'center', gap: '.375rem' }}>
|
<Stack sx={{ flexDirection: 'row', alignItems: 'center', gap: '.375rem' }}>
|
||||||
<ExportSvg color="#4a5565" strokeWidth={'.33'} size="1rem" />
|
<ExportSvg color="#4a5565" strokeWidth={'.33'} size="1rem" />
|
||||||
<span>{props?.text || 'Экспорт в Excel'}</span>
|
<span>{props?.text || 'Экспорт'}</span>
|
||||||
</Stack>
|
</Stack>
|
||||||
</DefaultOutlinedButton>
|
</DefaultOutlinedButton>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -21,6 +21,7 @@ const CollapsibleTree = ({
|
|||||||
placeholderSelect = 'Скрыто столбцов: ',
|
placeholderSelect = 'Скрыто столбцов: ',
|
||||||
selectedIds = [],
|
selectedIds = [],
|
||||||
onSelectNode,
|
onSelectNode,
|
||||||
|
forStage = false
|
||||||
}) => {
|
}) => {
|
||||||
const [expandedItems, setExpandedItems] = useState([]);
|
const [expandedItems, setExpandedItems] = useState([]);
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
@ -144,7 +145,7 @@ const CollapsibleTree = ({
|
|||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectedCount = selectedNodes.length - 1; //1 колонка всегда скрыта
|
const selectedCount = forStage ? selectedNodes.length : selectedNodes.length - 1; //1 колонка всегда скрыта
|
||||||
|
|
||||||
const isAllSelected = () => {
|
const isAllSelected = () => {
|
||||||
if (!columnTree) return false;
|
if (!columnTree) return false;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { DIRECTION_TRANSLATE, FORM_TYPE_OPTIONS } from '../../../constants';
|
||||||
import {
|
import {
|
||||||
Section,
|
Section,
|
||||||
SectionStartPart,
|
SectionStartPart,
|
||||||
@ -22,7 +23,7 @@ const TableCard = ({ table, onNavigate }) => {
|
|||||||
</div>
|
</div>
|
||||||
<SectionTitle>
|
<SectionTitle>
|
||||||
<h2>{table.name}</h2>
|
<h2>{table.name}</h2>
|
||||||
<h2>{table?.direction}</h2>
|
<h2>{DIRECTION_TRANSLATE[table?.direction]}</h2>
|
||||||
{/* <UsersList>
|
{/* <UsersList>
|
||||||
<div className="img-user"></div>
|
<div className="img-user"></div>
|
||||||
</UsersList> */}
|
</UsersList> */}
|
||||||
|
|||||||
@ -37,6 +37,7 @@ export const EDIT_ACCESS_RUSSIAN = {
|
|||||||
|
|
||||||
export const FORM_TYPE_OPTIONS = ['FORM_1', 'FORM_2', 'FORM_3', 'FORM_4'];
|
export const FORM_TYPE_OPTIONS = ['FORM_1', 'FORM_2', 'FORM_3', 'FORM_4'];
|
||||||
export const FROM_TYPE_TRANSLATE = { 'FORM_1': 'Смета ССП', 'FORM_2': 'Смета РФ', 'FORM_3': 'Программа развитие ПРРС', 'FORM_4': 'Проектная деятельность' };
|
export const FROM_TYPE_TRANSLATE = { 'FORM_1': 'Смета ССП', 'FORM_2': 'Смета РФ', 'FORM_3': 'Программа развитие ПРРС', 'FORM_4': 'Проектная деятельность' };
|
||||||
|
export const DIRECTION_TRANSLATE = { 'Support': 'Поддержка', 'Development': 'Развитие' };
|
||||||
|
|
||||||
export const ORG_UNIT_TYPE_OPTIONS = [
|
export const ORG_UNIT_TYPE_OPTIONS = [
|
||||||
{ value: 'ssp', label: 'ССП' },
|
{ value: 'ssp', label: 'ССП' },
|
||||||
|
|||||||
@ -22,7 +22,7 @@ import { ModalCreateProject } from './ModalCreateProject';
|
|||||||
import { ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions';
|
import { ExportWithTextButton } from '../../components/common/Buttons/ButtonsActions';
|
||||||
import { exportSingleForm } from '../../utils/exportForm';
|
import { exportSingleForm } from '../../utils/exportForm';
|
||||||
import TablesList from '../../components/common/TableList/TableList';
|
import TablesList from '../../components/common/TableList/TableList';
|
||||||
import { FROM_TYPE_TRANSLATE, SHEET_NAME } from '../../constants';
|
import { DIRECTION_TRANSLATE, FROM_TYPE_TRANSLATE, SHEET_NAME } from '../../constants';
|
||||||
|
|
||||||
const TaskInfo = ({ task }) => {
|
const TaskInfo = ({ task }) => {
|
||||||
const portalContent = (
|
const portalContent = (
|
||||||
@ -61,9 +61,10 @@ export default function TaskPage() {
|
|||||||
const mappedTables = data.result.all_sheets.map(sheet => ({
|
const mappedTables = data.result.all_sheets.map(sheet => ({
|
||||||
name: SHEET_NAME[sheet.sheet_name] || sheet.sheet_name,
|
name: SHEET_NAME[sheet.sheet_name] || sheet.sheet_name,
|
||||||
sheet: sheet.sheet_name,
|
sheet: sheet.sheet_name,
|
||||||
direction: sheet.direction
|
direction: sheet.direction,
|
||||||
}));
|
}));
|
||||||
setTables(mappedTables);
|
//пока скрыт
|
||||||
|
setTables(mappedTables.filter(t => t.sheet !== "OTCH9F",));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(
|
toast.error(
|
||||||
error?.response?.data?.detail || 'Ошибка получения данных задачи',
|
error?.response?.data?.detail || 'Ошибка получения данных задачи',
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import {
|
|||||||
FORM_TYPE_OPTIONS,
|
FORM_TYPE_OPTIONS,
|
||||||
ORG_UNIT_TYPE_OPTIONS,
|
ORG_UNIT_TYPE_OPTIONS,
|
||||||
ROLES_NAME_ID,
|
ROLES_NAME_ID,
|
||||||
|
FROM_TYPE_TRANSLATE
|
||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
import { useAuth } from '../../app/context/AuthProvider';
|
import { useAuth } from '../../app/context/AuthProvider';
|
||||||
import { SspApi } from '../../api/ssp';
|
import { SspApi } from '../../api/ssp';
|
||||||
@ -129,8 +130,8 @@ export default function TasksPage() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast.error(
|
toast.error(
|
||||||
error.response?.data?.detail ||
|
error.response?.data?.detail ||
|
||||||
error.response?.data?.message ||
|
error.response?.data?.message ||
|
||||||
'Ошибка при создании задачи',
|
'Ошибка при создании задачи',
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
setLoadingUpdate(false);
|
setLoadingUpdate(false);
|
||||||
@ -182,7 +183,7 @@ export default function TasksPage() {
|
|||||||
const res = await TasksApi.list(params);
|
const res = await TasksApi.list(params);
|
||||||
const data = res.result;
|
const data = res.result;
|
||||||
|
|
||||||
|
|
||||||
setItems(data);
|
setItems(data);
|
||||||
setTotalCount(res.count);
|
setTotalCount(res.count);
|
||||||
|
|
||||||
@ -413,7 +414,7 @@ export default function TasksPage() {
|
|||||||
onChange={(e) => handleFormChange('formTypeCode', e.target.value)}
|
onChange={(e) => handleFormChange('formTypeCode', e.target.value)}
|
||||||
renderValue={(selected) =>
|
renderValue={(selected) =>
|
||||||
selected ? (
|
selected ? (
|
||||||
selected
|
FROM_TYPE_TRANSLATE[selected]
|
||||||
) : (
|
) : (
|
||||||
<span style={{ color: 'rgba(0,0,0,0.6)' }}>Не выбрана</span>
|
<span style={{ color: 'rgba(0,0,0,0.6)' }}>Не выбрана</span>
|
||||||
)
|
)
|
||||||
@ -423,7 +424,7 @@ export default function TasksPage() {
|
|||||||
>
|
>
|
||||||
{FORM_TYPE_OPTIONS.map((formType) => (
|
{FORM_TYPE_OPTIONS.map((formType) => (
|
||||||
<MenuItem key={formType} value={formType}>
|
<MenuItem key={formType} value={formType}>
|
||||||
{formType}
|
{FROM_TYPE_TRANSLATE[formType]}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user