Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

shopinvader_schema_sale: clean schema naming #8

Open
wants to merge 15 commits into
base: 16.0-add-api-sale
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions __init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .amount import SaleAmount, SaleLineAmount
from .cart import CartResponse, CartSyncInput, CartTransaction
from .sale_order_line import SaleOrderLine
6 changes: 6 additions & 0 deletions setup/shopinvader_api_sale/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
6 changes: 6 additions & 0 deletions setup/shopinvader_schema_sale/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
39 changes: 30 additions & 9 deletions shopinvader_api_cart/routers/cart.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
)
from odoo.addons.sale.models.sale_order import SaleOrder
from odoo.addons.sale.models.sale_order_line import SaleOrderLine
from odoo.addons.shopinvader_schema_sale.schemas import Sale

from ..schemas import CartResponse, CartSyncInput, CartTransaction
from ..schemas import CartSyncInput, CartTransaction, CartUpdateInput

cart_router = APIRouter(tags=["carts"])

Expand All @@ -28,12 +29,12 @@ def get(
env: Annotated[api.Environment, Depends(authenticated_partner_env)],
partner: Annotated["ResPartner", Depends(authenticated_partner)],
uuid: str | None = None,
) -> CartResponse | None:
) -> Sale | None:
"""
Return an empty dict if no cart was found
"""
cart = env["sale.order"]._find_open_cart(partner.id, uuid)
return CartResponse.from_cart(cart) if cart else None
return Sale.from_sale_order(cart) if cart else None


@cart_router.post("/sync", status_code=201)
Expand All @@ -43,17 +44,30 @@ def sync(
env: Annotated[api.Environment, Depends(authenticated_partner_env)],
partner: Annotated["ResPartner", Depends(authenticated_partner)],
uuid: str | None = None,
) -> CartResponse | None:
) -> Sale | None:
cart = env["sale.order"]._find_open_cart(partner.id, uuid)
cart = env["shopinvader_api_cart.service.helper"]._sync_cart(
cart = env["shopinvader_api_cart.cart_router.helper"]._sync_cart(
partner, cart, uuid, data.transactions
)
return CartResponse.from_cart(cart) if cart else None
return Sale.from_sale_order(cart) if cart else None


class ShopinvaderApiCartServiceHelper(models.AbstractModel):
_name = "shopinvader_api_cart.service.helper"
_description = "ShopInvader API Cart Service Helper"
@cart_router.post("/update")
@cart_router.post("/update/{uuid}")
def update(
data: CartUpdateInput,
env: Annotated[api.Environment, Depends(authenticated_partner_env)],
partner: Annotated["ResPartner", Depends(authenticated_partner)],
uuid: str | None = None,
) -> Sale:
cart = env["shopinvader_api_cart.cart_router.helper"]._update(partner, data, uuid)

return Sale.from_sale_order(cart)


class ShopinvaderApiCartRouterHelper(models.AbstractModel):
_name = "shopinvader_api_cart.cart_router.helper"
_description = "ShopInvader API Cart Router Helper"

@api.model
def _check_transactions(self, transactions: list[CartTransaction]):
Expand Down Expand Up @@ -210,3 +224,10 @@ def _sync_cart(
# * cart_uuid = cart.uuid: Existing cart and transaction for this cart
self._apply_transactions(cart, transactions)
return cart

def _update(self, partner, data, uuid):
cart = self.env["sale.order"]._find_open_cart(partner.id, uuid)
if not cart:
cart = self.env["sale.order"]._create_empty_cart()
cart.write(data.convert_to_sale_write())
return cart
10 changes: 7 additions & 3 deletions shopinvader_api_cart/schemas/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
from .amount import SaleAmount, SaleLineAmount
from .cart import CartTransaction, CartSyncInput, CartResponse
from .sale_order_line import SaleOrderLine
from .cart import (
CartTransaction,
CartSyncInput,
CartUpdateInput,
ShippingUpdateInfo,
InvoicingUpdateInfo,
)
67 changes: 24 additions & 43 deletions shopinvader_api_cart/schemas/cart.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from datetime import datetime
from typing import List

from extendable_pydantic import StrictExtendableBaseModel

from odoo.addons.shopinvader_schema_address.schemas import (
BillingAddress,
ShippingAddress,
)

from .amount import SaleAmount
from .sale_order_line import SaleOrderLine


class CartTransaction(StrictExtendableBaseModel):
uuid: str | None = None
Expand All @@ -25,39 +16,29 @@ class CartSyncInput(StrictExtendableBaseModel):
transactions: List[CartTransaction]


class CartResponse(StrictExtendableBaseModel):
uuid: str | None = None
id: int
state: str
name: str
date_order: datetime
lines: List[SaleOrderLine]
amount: SaleAmount | None = None
delivery: ShippingAddress | None = None
invoicing: BillingAddress | None = None
class ShippingUpdateInfo(StrictExtendableBaseModel):
address_id: int


class InvoicingUpdateInfo(StrictExtendableBaseModel):
address_id: int


class CartUpdateInput(StrictExtendableBaseModel):
client_order_ref: str | None = None
delivery: ShippingUpdateInfo | None = None
invoicing: InvoicingUpdateInfo | None = None
note: str | None = None

@classmethod
def from_cart(cls, odoo_rec):
return cls.model_construct(
uuid=odoo_rec.uuid or None,
id=odoo_rec.id,
state=odoo_rec.state,
name=odoo_rec.name,
date_order=odoo_rec.date_order,
lines=[
SaleOrderLine.from_sale_order_line(line) for line in odoo_rec.order_line
],
amount=SaleAmount.from_sale_order(odoo_rec),
delivery=(
ShippingAddress.from_res_partner(odoo_rec.partner_shipping_id)
if odoo_rec.partner_shipping_id
else None
),
invoicing=(
BillingAddress.from_res_partner(odoo_rec.partner_invoice_id)
if odoo_rec.partner_invoice_id
else None
),
note=odoo_rec.note or None,
)
def convert_to_sale_write(self):
vals = {}
data = self.model_dump(exclude_unset=True)
if "client_order_ref" in data:
vals["client_order_ref"] = data["client_order_ref"]
if "note" in data:
vals["note"] = data["note"]
if (data.get("delivery") or {}).get("address_id"):
vals["partner_shipping_id"] = data["delivery"]["address_id"]
if (data.get("invoicing") or {}).get("address_id"):
vals["partner_invoicing_id"] = data["invoicing"]["address_id"]
return vals
23 changes: 22 additions & 1 deletion shopinvader_api_cart/tests/test_shopinvader_api_cart.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ def test_multi_transactions_multi_products_mix_create_update(self) -> None:
self.default_fastapi_authenticated_partner.id
)
create_line_vals = self.env[
"shopinvader_api_cart.service.helper"
"shopinvader_api_cart.cart_router.helper"
]._apply_transactions_creating_new_cart_line(
so, [CartTransaction(uuid="uuid1", product_id=self.product_1.id, qty=1)]
)
Expand Down Expand Up @@ -420,3 +420,24 @@ def test_multi_transactions_multi_products_mix_create_update(self) -> None:
)
self.assertEqual(2, line_product_2_id.product_uom_qty)
self.assertEqual(so.applied_cart_api_transaction_uuids, "uuid2,uuid3,uuid4")

def test_update(self) -> None:
partner = self.default_fastapi_authenticated_partner
address = self.env["res.partner"].create(
{
"name": "Shipping",
"parent_id": partner.id,
"type": "delivery",
}
)
so = self.env["sale.order"]._create_empty_cart(
self.default_fastapi_authenticated_partner.id
)
data = {"delivery": {"address_id": address.id}}
with self._create_test_client(router=cart_router) as test_client:
response: Response = test_client.post(
f"/update/{so.uuid}", content=json.dumps(data)
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(so.partner_shipping_id, address)
self.assertEqual(so.partner_invoice_id, partner)
1 change: 1 addition & 0 deletions shopinvader_api_sale/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import routers
25 changes: 25 additions & 0 deletions shopinvader_api_sale/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright 2023 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).


{
"name": "Shopinvader API Sale",
"summary": "Sale FastApi for exposing sale order",
"version": "16.0.1.0.0",
"development_status": "Alpha",
"category": "Uncategorized",
"website": "https://github.com/shopinvader/odoo-shopinvader",
"author": " Akretion",
"license": "AGPL-3",
"external_dependencies": {
"python": [],
"bin": [],
},
"depends": [
"shopinvader_schema_sale",
"shopinvader_api_cart", # TODO access right
],
"data": [],
"demo": [],
}
1 change: 1 addition & 0 deletions shopinvader_api_sale/routers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .sales import sale_router
76 changes: 76 additions & 0 deletions shopinvader_api_sale/routers/sales.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Copyright 2023 Akretion (https://www.akretion.com).
# @author Sébastien BEAU <[email protected]>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).


from typing import Annotated

from fastapi import APIRouter, Depends

from odoo import api, models

from odoo.addons.base.models.res_partner import Partner as ResPartner
from odoo.addons.extendable_fastapi.schemas import PagedCollection
from odoo.addons.fastapi.dependencies import (
authenticated_partner,
authenticated_partner_env,
paging,
)
from odoo.addons.fastapi.schemas import Paging
from odoo.addons.fastapi.utils import FilteredDomainAdapter
from odoo.addons.shopinvader_schema_sale.schemas import Sale, SaleSearch

sale_router = APIRouter(tags=["sales"])


@sale_router.get("/sales")
def search(
params: Annotated[SaleSearch, Depends()],
paging: Annotated[Paging, Depends(paging)],
env: Annotated[api.Environment, Depends(authenticated_partner_env)],
partner: Annotated["ResPartner", Depends(authenticated_partner)],
) -> PagedCollection[Sale]:
"""Get the list of sale orders."""
count, orders = env["shopinvader_api_sale.sales_router.helper"]._search(
paging, params
)
return PagedCollection[Sale](
count=count,
items=[Sale.from_sale_order(order) for order in orders],
)


@sale_router.get("/sales/{sale_id}")
def get(
sale_id: int,
env: Annotated[api.Environment, Depends(authenticated_partner_env)],
partner: Annotated["ResPartner", Depends(authenticated_partner)],
) -> Sale:
"""
Get sale order of authenticated user with specific sale_id
sale corresponds to authenticated partner
"""
return Sale.from_sale_order(
env["shopinvader_api_sale.sales_router.helper"]._get(sale_id)
)


class ShopinvaderApiSaleSalesRouterHelper(models.AbstractModel):
_name = "shopinvader_api_sale.sales_router.helper"
_description = "Shopinvader Api Sale Service Helper"

@property
def adapter(self):
return FilteredDomainAdapter(
self.env["sale.order"], [("typology", "=", "sale")]
)

def _get(self, record_id):
return self.adapter.get(record_id)

def _search(self, paging, params):
return self.adapter.search_with_count(
params.to_odoo_domain(),
limit=paging.limit,
offset=paging.offset,
)
1 change: 1 addition & 0 deletions shopinvader_api_sale/tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import test_shopinvader_sale_api
Loading
Loading