diff --git a/modules/fulfillment_base/models/__init__.py b/modules/fulfillment_base/models/__init__.py
index cfa5105..1420f30 100644
--- a/modules/fulfillment_base/models/__init__.py
+++ b/modules/fulfillment_base/models/__init__.py
@@ -14,3 +14,4 @@ from .deal_product import (
from .deal_service import DealService as DealService
from .product import Product as Product
from .service import Service as Service, ServiceCategory as ServiceCategory
+from .marketplace import BaseMarketplace as BaseMarketplace, Marketplace as Marketplace
diff --git a/modules/fulfillment_base/models/marketplace.py b/modules/fulfillment_base/models/marketplace.py
new file mode 100644
index 0000000..bc79b28
--- /dev/null
+++ b/modules/fulfillment_base/models/marketplace.py
@@ -0,0 +1,32 @@
+from typing import TYPE_CHECKING
+
+from sqlalchemy import ForeignKey, JSON
+from sqlalchemy.orm import Mapped, mapped_column, relationship
+
+from models.base import BaseModel
+from models.mixins import IdMixin, SoftDeleteMixin
+
+if TYPE_CHECKING:
+ from modules.clients.models import Client
+
+
+class BaseMarketplace(BaseModel, IdMixin):
+ __tablename__ = "fulfillment_base_base_marketplaces"
+
+ name: Mapped[str] = mapped_column()
+ icon_url: Mapped[str] = mapped_column()
+
+
+class Marketplace(BaseModel, IdMixin, SoftDeleteMixin):
+ __tablename__ = "fulfillment_base_marketplaces"
+
+ base_marketplace_id: Mapped[str] = mapped_column(
+ ForeignKey("fulfillment_base_base_marketplaces.id")
+ )
+ base_marketplace: Mapped["BaseMarketplace"] = relationship(lazy="joined")
+
+ client_id: Mapped[int] = mapped_column(ForeignKey("clients.id"))
+ client: Mapped["Client"] = relationship()
+
+ name: Mapped[str] = mapped_column()
+ auth_data: Mapped[dict] = mapped_column(type_=JSON)
diff --git a/modules/fulfillment_base/repositories/__init__.py b/modules/fulfillment_base/repositories/__init__.py
index 3385055..5f39d8c 100644
--- a/modules/fulfillment_base/repositories/__init__.py
+++ b/modules/fulfillment_base/repositories/__init__.py
@@ -1,8 +1,9 @@
-from .deal_product import DealProductRepository as DealProductRepository
-from .product_service import ProductServiceRepository as ProductServiceRepository
-from .deal_service import DealServiceRepository as DealServiceRepository
-from .product import ProductRepository as ProductRepository
-from .service import ServiceRepository as ServiceRepository
-from .services_kit import ServicesKitRepository as ServicesKitRepository
-from .service_category import ServiceCategoryRepository as ServiceCategoryRepository
from .barcode_template import BarcodeTemplateRepository as BarcodeTemplateRepository
+from .deal_product import DealProductRepository as DealProductRepository
+from .deal_service import DealServiceRepository as DealServiceRepository
+from .marketplace import MarketplaceRepository as MarketplaceRepository
+from .product import ProductRepository as ProductRepository
+from .product_service import ProductServiceRepository as ProductServiceRepository
+from .service import ServiceRepository as ServiceRepository
+from .service_category import ServiceCategoryRepository as ServiceCategoryRepository
+from .services_kit import ServicesKitRepository as ServicesKitRepository
diff --git a/modules/fulfillment_base/repositories/marketplace.py b/modules/fulfillment_base/repositories/marketplace.py
new file mode 100644
index 0000000..7ee67dc
--- /dev/null
+++ b/modules/fulfillment_base/repositories/marketplace.py
@@ -0,0 +1,62 @@
+from sqlalchemy.orm import joinedload
+
+from modules.clients.models import Client
+from modules.fulfillment_base.models import (
+ Marketplace,
+ BaseMarketplace,
+)
+from modules.fulfillment_base.schemas.marketplace import (
+ CreateMarketplaceSchema,
+ UpdateMarketplaceSchema,
+)
+from repositories.mixins import *
+
+
+class MarketplaceRepository(
+ RepCrudMixin[Marketplace, CreateMarketplaceSchema, UpdateMarketplaceSchema],
+):
+ session: AsyncSession
+ entity_class = Marketplace
+ entity_not_found_msg = "Маркетплейс не найден"
+
+ def _process_get_all_stmt_with_args(self, stmt: Select, *args) -> Select:
+ client_id: int = args[0]
+ return (
+ stmt.options(
+ joinedload(Marketplace.base_marketplace),
+ joinedload(Marketplace.client),
+ )
+ .where(
+ Marketplace.is_deleted.is_(False), Marketplace.client_id == client_id
+ )
+ .order_by(Marketplace.id)
+ )
+
+ def _process_get_by_id_stmt(self, stmt: Select) -> Select:
+ return stmt.options(
+ joinedload(Marketplace.base_marketplace), joinedload(Marketplace.client)
+ )
+
+ async def get_base_marketplaces(self) -> list[BaseMarketplace]:
+ stmt = select(BaseMarketplace)
+ result = await self.session.execute(stmt)
+ return list(result.scalars().all())
+
+ async def _prepare_create(self, data: CreateMarketplaceSchema) -> dict:
+ dict_data = data.model_dump()
+ dict_data["base_marketplace_id"] = data.base_marketplace.id
+ del dict_data["base_marketplace"]
+ dict_data["client_id"] = data.client.id
+ del dict_data["client"]
+ return dict_data
+
+ async def update(
+ self, template: Marketplace, data: UpdateMarketplaceSchema
+ ) -> Marketplace:
+ if data.base_marketplace:
+ data.base_marketplace = BaseMarketplace(
+ **data.base_marketplace.model_dump()
+ )
+ if data.client:
+ data.client = Client(**data.client.model_dump())
+ return await self._apply_update_data_to_model(template, data, True)
diff --git a/modules/fulfillment_base/routers/marketplace.py b/modules/fulfillment_base/routers/marketplace.py
new file mode 100644
index 0000000..72d623d
--- /dev/null
+++ b/modules/fulfillment_base/routers/marketplace.py
@@ -0,0 +1,66 @@
+from fastapi import APIRouter, Path
+
+from backend.dependecies import SessionDependency
+from modules.fulfillment_base.schemas.marketplace import *
+from modules.fulfillment_base.services import MarketplaceService
+
+router = APIRouter(tags=["marketplace"])
+
+
+@router.get(
+ "/base",
+ response_model=GetBaseMarketplacesResponse,
+ operation_id="get_base_marketplaces",
+)
+async def get_base_marketplaces(
+ session: SessionDependency,
+):
+ return await MarketplaceService(session).get_base_marketplaces()
+
+
+@router.get(
+ "/{clientId}",
+ response_model=GetMarketplacesResponse,
+ operation_id="get_marketplaces",
+)
+async def get_marketplaces(
+ session: SessionDependency, client_id: int = Path(alias="clientId")
+):
+ return await MarketplaceService(session).get_all(client_id)
+
+
+@router.post(
+ "/",
+ response_model=CreateMarketplaceResponse,
+ operation_id="create_marketplace",
+)
+async def create_product(
+ session: SessionDependency,
+ request: CreateMarketplaceRequest,
+):
+ return await MarketplaceService(session).create(request)
+
+
+@router.patch(
+ "/{pk}",
+ response_model=UpdateMarketplaceResponse,
+ operation_id="update_marketplace",
+)
+async def update_marketplace(
+ session: SessionDependency,
+ request: UpdateMarketplaceRequest,
+ pk: int = Path(),
+):
+ return await MarketplaceService(session).update(pk, request)
+
+
+@router.delete(
+ "/{pk}",
+ response_model=DeleteMarketplaceResponse,
+ operation_id="delete_marketplace",
+)
+async def delete_marketplace(
+ session: SessionDependency,
+ pk: int = Path(),
+):
+ return await MarketplaceService(session).delete(pk)
diff --git a/modules/fulfillment_base/schemas/marketplace.py b/modules/fulfillment_base/schemas/marketplace.py
new file mode 100644
index 0000000..712b0a7
--- /dev/null
+++ b/modules/fulfillment_base/schemas/marketplace.py
@@ -0,0 +1,77 @@
+from typing import Optional
+
+from modules.clients.schemas.client import ClientSchema
+from schemas.base import BaseSchema, BaseResponse
+
+
+# region Entity
+
+
+class BaseMarketplaceSchema(BaseSchema):
+ id: int
+ name: str
+ icon_url: str
+
+
+class MarketplaceSchema(BaseSchema):
+ id: int
+ base_marketplace_id: int
+ base_marketplace: BaseMarketplaceSchema
+ client: ClientSchema
+ name: str
+ auth_data: dict
+
+
+class CreateMarketplaceSchema(BaseSchema):
+ base_marketplace: BaseMarketplaceSchema
+ client: ClientSchema
+ name: str
+ auth_data: dict
+
+
+class UpdateMarketplaceSchema(BaseSchema):
+ base_marketplace: Optional[BaseMarketplaceSchema] = None
+ client: Optional[ClientSchema] = None
+ name: Optional[str] = None
+ auth_data: Optional[dict] = None
+
+
+# endregion
+
+# region Request
+
+
+class CreateMarketplaceRequest(BaseSchema):
+ entity: CreateMarketplaceSchema
+
+
+class UpdateMarketplaceRequest(BaseSchema):
+ entity: UpdateMarketplaceSchema
+
+
+# endregion
+
+# region Response
+
+
+class GetBaseMarketplacesResponse(BaseSchema):
+ items: list[BaseMarketplaceSchema]
+
+
+class GetMarketplacesResponse(BaseSchema):
+ items: list[MarketplaceSchema]
+
+
+class CreateMarketplaceResponse(BaseResponse):
+ entity: MarketplaceSchema
+
+
+class UpdateMarketplaceResponse(BaseResponse):
+ pass
+
+
+class DeleteMarketplaceResponse(BaseResponse):
+ pass
+
+
+# endregion
diff --git a/modules/fulfillment_base/services/__init__.py b/modules/fulfillment_base/services/__init__.py
index e4d4210..ae69787 100644
--- a/modules/fulfillment_base/services/__init__.py
+++ b/modules/fulfillment_base/services/__init__.py
@@ -7,3 +7,4 @@ from .services_kit import ServicesKitService as ServicesKitService
from .service_category import ServiceCategoryService as ServiceCategoryService
from .barcode_template import BarcodeTemplateService as BarcodeTemplateService
from .barcode_printer_service import BarcodePrinterService as BarcodePrinterService
+from .marketplace import MarketplaceService as MarketplaceService
diff --git a/modules/fulfillment_base/services/barcode_template.py b/modules/fulfillment_base/services/barcode_template.py
index 4958e21..a7543d2 100644
--- a/modules/fulfillment_base/services/barcode_template.py
+++ b/modules/fulfillment_base/services/barcode_template.py
@@ -20,9 +20,6 @@ class BarcodeTemplateService(
def __init__(self, session: AsyncSession):
self.repository = BarcodeTemplateRepository(session)
- async def is_soft_delete(self, template: BarcodeTemplate) -> bool:
- return True
-
async def get_attributes(self) -> GetBarcodeAttributesResponse:
attributes = await self.repository.get_attributes()
return GetBarcodeAttributesResponse(
diff --git a/modules/fulfillment_base/services/marketplace.py b/modules/fulfillment_base/services/marketplace.py
new file mode 100644
index 0000000..24f2764
--- /dev/null
+++ b/modules/fulfillment_base/services/marketplace.py
@@ -0,0 +1,27 @@
+from modules.fulfillment_base.models import Marketplace
+from modules.fulfillment_base.repositories import MarketplaceRepository
+from modules.fulfillment_base.schemas.marketplace import *
+from services.mixins import *
+
+
+class MarketplaceService(
+ ServiceCrudMixin[
+ Marketplace,
+ MarketplaceSchema,
+ CreateMarketplaceRequest,
+ UpdateMarketplaceRequest,
+ ]
+):
+ schema_class = MarketplaceSchema
+ entity_deleted_msg = "Маркетплейс успешно удален"
+ entity_updated_msg = "Маркетплейс успешно обновлен"
+ entity_created_msg = "Маркетплейс успешно создан"
+
+ def __init__(self, session: AsyncSession):
+ self.repository = MarketplaceRepository(session)
+
+ async def get_base_marketplaces(self) -> GetBaseMarketplacesResponse:
+ mps = await self.repository.get_base_marketplaces()
+ return GetBaseMarketplacesResponse(
+ items=[BaseMarketplaceSchema.model_validate(mp) for mp in mps]
+ )
diff --git a/static/icons/denco.png b/static/icons/denco.png
new file mode 100644
index 0000000..37146e7
Binary files /dev/null and b/static/icons/denco.png differ
diff --git a/static/icons/denco.svg b/static/icons/denco.svg
new file mode 100644
index 0000000..c76ad6e
--- /dev/null
+++ b/static/icons/denco.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/static/icons/ozon.png b/static/icons/ozon.png
new file mode 100644
index 0000000..1effefc
Binary files /dev/null and b/static/icons/ozon.png differ
diff --git a/static/icons/ozon.svg b/static/icons/ozon.svg
new file mode 100644
index 0000000..0e2622b
--- /dev/null
+++ b/static/icons/ozon.svg
@@ -0,0 +1,114 @@
+
+
+
diff --git a/static/icons/wb.svg b/static/icons/wb.svg
new file mode 100644
index 0000000..3fc2817
--- /dev/null
+++ b/static/icons/wb.svg
@@ -0,0 +1,16 @@
+
diff --git a/static/icons/ym.png b/static/icons/ym.png
new file mode 100644
index 0000000..a19c517
Binary files /dev/null and b/static/icons/ym.png differ
diff --git a/static/icons/ym.svg b/static/icons/ym.svg
new file mode 100644
index 0000000..b90e1af
--- /dev/null
+++ b/static/icons/ym.svg
@@ -0,0 +1,36 @@
+
+
+