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

Shopfloor + Shopinvader :) #110

Draft
wants to merge 15 commits into
base: 14.0
Choose a base branch
from
Draft
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
6 changes: 6 additions & 0 deletions setup/shopfloor_shopinvader_base/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/shopfloor_shopinvader_mobile_base/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,
)
9 changes: 7 additions & 2 deletions shopfloor_mobile_base/static/wms/src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,15 @@ const router = new VueRouter({
});
router.beforeEach(async (to, from, next) => {
await Vue.nextTick();
// FIXME: for shopinvader to work we need a user.
// To have a user we must log in or have an api key.
// Hence, either we turn on login for demo mode
// or we find another solution.
// &&
// !router.app.demo_mode
if (
!router.app.is_authenticated() &&
to.meta.requiresAuth &&
!router.app.demo_mode
to.meta.requiresAuth
) {
next("login");
} else {
Expand Down
1 change: 1 addition & 0 deletions shopfloor_shopinvader_base/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wait for the bot ;)
4 changes: 4 additions & 0 deletions shopfloor_shopinvader_base/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import components
from . import controllers
from . import models
from . import services
22 changes: 22 additions & 0 deletions shopfloor_shopinvader_base/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Copyright 2021 Camptocamp SA (http://www.camptocamp.com)
# @author Simone Orsi <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

{
"name": "Shopfloor / Shopinvader integration",
"summary": "Provides base integration for Shopinvader into Shopfloor mobile app",
"version": "14.0.1.0.0",
"development_status": "Alpha",
"category": "E-commerce",
"website": "https://github.com/OCA/wms",
"author": "Camptocamp, Odoo Community Association (OCA)",
"maintainer": ["simahawk"],
"license": "AGPL-3",
"depends": ["shopfloor_base", "shopinvader", "sales_team"],
"data": ["views/shopfloor_app.xml"],
"demo": [
"demo/tech_user_demo.xml",
"demo/res_users_demo.xml",
"demo/shopfloor_app_demo.xml",
],
}
1 change: 1 addition & 0 deletions shopfloor_shopinvader_base/components/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import service_context_provider
61 changes: 61 additions & 0 deletions shopfloor_shopinvader_base/components/service_context_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Copyright 2021 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).


import logging

from odoo import _
from odoo.exceptions import MissingError

from odoo.addons.component.core import Component
from odoo.addons.shopinvader.utils import get_partner_work_context

_logger = logging.getLogger(__name__)


class ShopfloorInvaderServiceContextProvider(Component):
_name = "shopfloor.invader.service.context.provider"
_inherit = "base.rest.service.context.provider"
# No need to inherit from default ctx provider as it's all custom here
# _inherit = "shopinvader.service.context.provider"
_collection = "shopinvader.backend"
_usage = "shopfloor_invader_component_context_provider"

def _get_component_context(self):
assert self.collection._name == self._collection
res = super()._get_component_context()
res.update(
{
"shopinvader_backend": self.collection,
"shopinvader_session": self._get_shopinvader_session(),
}
)
res.update(get_partner_work_context(self._get_shopinvader_partner()))
return res

def _get_shopinvader_partner(self):
# As we use authenticated users the partner must come from current user.
# NOTE: this piece could probably go into a new shopinvader module
# but we want take full control on this ctx provider now.
partner_model = self.env["shopinvader.partner"]
# TODO: this could depend on having real users or not.
# For now, we rely on having a real user in Odoo.
# Note that this user != self.collection.env.user (tech user)
partner = self.request.env.user.partner_id
s_partner = partner._get_invader_partner(self.collection)
if s_partner:
return s_partner
else:
_logger.warning("Cannot determine shopinvader.partner")
raise MissingError(_("The given shopinvader user is not found!"))
return partner_model.browse()

def _get_shopinvader_session(self):
# HTTP_SESS are data that are store in the shopinvader session
# and forwarded to odoo at each request
# it allow to access to some specific field of the user session
# By security always force typing
# Note: rails cookies store session are serveless ;)
return {
"cart_id": int(self.request.httprequest.environ.get("HTTP_SESS_CART_ID", 0))
}
1 change: 1 addition & 0 deletions shopfloor_shopinvader_base/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import main
59 changes: 59 additions & 0 deletions shopfloor_shopinvader_base/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Copyright 2021 Camptocamp SA (http://www.camptocamp.com)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

import logging

from odoo.http import request

from odoo.addons.base_rest.controllers import main

_logger = logging.getLogger(__name__)


class ShopfloorInvaderController(main.RestController):

# TODO: this should be controlled from shopfloor.app ?
# We should probably generate other app-specific URLs via app._generate_endpoints
_component_context_provider = "shopfloor_invader_component_context_provider"

def _process_shopinvader_endpoint(
self,
app_id,
service_name,
service_method_name,
*args,
collection=None,
**kwargs
):
"""Wrapper for `_process_method` call.

Behavior is the same for the methods automatically
generated by `rest.service.registration`.
"""
collection = collection or request.env["shopfloor.app"].browse(app_id)
# get shopinvader backend from current app
backend = collection.shopinvader_backend_id.with_context(
shopfloor_app=collection.tech_name
)
if collection.shopinvader_tech_user_id:
# Use a technical user to bypass issues like
#
# odoo.exceptions.AccessError:
# You are not allowed to access 'Sales Team' (crm.team) records.
# This operation is allowed for the following groups:
# - Sales/Administrator
# - Sales/User: Own Documents Only
# - User types/Internal User
#
# If you want to support real users updates you must give them proper rights.
backend = backend.with_user(collection.shopinvader_tech_user_id)
if not backend:
# A not found will be raised later, just leave a trace for the poor devs :)
_logger.error(
"No shopinvader backend found for collection: %s", str(collection)
)
# TODO: in base_rest `*args` is passed based on the type of route
# (eg: /<int:id>/update)
return self._process_method(
service_name, service_method_name, *args, collection=backend, params=kwargs
)
24 changes: 24 additions & 0 deletions shopfloor_shopinvader_base/demo/res_users_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<odoo>
<record id="partner_1" model="res.partner">
<field name="name">Johnny</field>
<field name="email">[email protected]</field>
<field name="street">Somewhere</field>
<field name="city">Nowhere</field>
<field name="zip">1110</field>
<field name="country_id" ref="base.fr" />
<field name="property_product_pricelist" ref="product.list0" />
</record>

<record id="shopinvader_partner_1" model="shopinvader.partner">
<field name="backend_id" ref="shopinvader.backend_1" />
<field name="external_id">AaAaA</field>
<field name="record_id" ref="partner_1" />
</record>

<record id="user_demo" model="res.users">
<field name="groups_id" eval="[(4, ref('base.group_portal'))]" />
<field name="partner_id" ref="partner_1" />
<field name="login">shopfloor.invader.demo</field>
<field name="password">12345</field>
</record>
</odoo>
10 changes: 10 additions & 0 deletions shopfloor_shopinvader_base/demo/shopfloor_app_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<odoo>
<record id="app_demo" model="shopfloor.app">
<field name="name">shopinvader demo</field>
<field name="short_name">Shop</field>
<field name="tech_name">shop_demo</field>
<field name="category">shop</field>
<field name="shopinvader_tech_user_id" ref="invader_tech_user_demo" />
<field name="shopinvader_backend_id" ref="shopinvader.backend_1" />
</record>
</odoo>
16 changes: 16 additions & 0 deletions shopfloor_shopinvader_base/demo/tech_user_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<odoo>
<record id="invader_tech_user_demo" model="res.users">
<field
name="groups_id"
eval="[(6,0,[
ref('base.group_user'),
ref('base.group_system'),
ref('shopinvader.group_shopinvader_manager'),
ref('sales_team.group_sale_manager'),
])]"
/>
<field name="name">shopfloor shopinvader technical user</field>
<field name="email">[email protected]</field>
<field name="login">shopfloor.invader.tech.demo</field>
</record>
</odoo>
1 change: 1 addition & 0 deletions shopfloor_shopinvader_base/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import shopfloor_app
65 changes: 65 additions & 0 deletions shopfloor_shopinvader_base/models/shopfloor_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright 2021 Camptocamp SA (http://www.camptocamp.com)
# @author Simone Orsi <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
from odoo import api, fields, models

from odoo.addons.component.core import _component_databases

from ..controllers.main import ShopfloorInvaderController


class ShopfloorApp(models.Model):
_inherit = "shopfloor.app"

category = fields.Selection(selection_add=[("shop", "Shop")])
shopinvader_backend_id = fields.Many2one(
comodel_name="shopinvader.backend",
string="Shopinvader Backend",
)
shopinvader_tech_user_id = fields.Many2one(
comodel_name="res.users",
string="Shopinvader Tech User",
help="""
If this user is not set,
you'll have to ensure that every app user calling shop services
is allowed to handle sale records (eg: sale.order write).
""",
)

shop_api_route = fields.Char(compute="_compute_shop_api_route")

def _register_endpoints(self):
super()._register_endpoints()
self._register_shop_endpoints()

def _register_shop_endpoints(self):
services = self._get_shop_services()
for service in services:
self._prepare_non_decorated_endpoints(service)
self._generate_shop_endpoints(service)
self.env["rest.service.registration"]._register_rest_route(self.shop_api_route)

def _is_component_registry_ready(self):
# TODO: this can be moved to shopfloor_base
comp_registry = _component_databases.get(self.env.cr.dbname)
return comp_registry and comp_registry.ready

def _get_shop_services(self):
if not self._is_component_registry_ready():
return []
return self.env["rest.service.registration"]._get_services(
"shopinvader.backend"
)

@api.depends("api_route")
def _compute_shop_api_route(self):
for rec in self:
rec.shop_api_route = rec.api_route.rstrip("/") + "/invader/"

def _generate_shop_endpoints(self, service):
values = self._generate_endpoints_values(service, self.shop_api_route)
for vals in values:
rest_endpoint_handler = (
ShopfloorInvaderController()._process_shopinvader_endpoint
)
self._generate_endpoints_routes(service, rest_endpoint_handler, vals)
1 change: 1 addition & 0 deletions shopfloor_shopinvader_base/services/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import product
54 changes: 54 additions & 0 deletions shopfloor_shopinvader_base/services/product.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright 2019 Camptocamp SA (http://www.camptocamp.com)
# @author: [email protected]
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).

from odoo.addons.component.core import Component

# TODO: this is something I mande in 2019 in the ctx of a PoC for COS
# here https://github.com/camptocamp/odoo-shopinvader/
# tree/add-shopinvader_rest_product/shopinvader_rest_product
# should be moved to its own module in odoo-shopinvader if we keep it.


class ProductService(Component):
_inherit = "base.shopinvader.service"
_name = "shopinvader.product.service"
_usage = "products"
_expose_model = "shopinvader.variant"

@property
def _exposed_model(self):
# TODO: do we care at all about permissions here?
# I think not. This is correct till we access product info properly.
# It would be better probably to have a specific RR?
return super()._exposed_model.sudo()

# The following method are 'public' and can be called from the controller.
def get(self, _id):
return self._to_json(self._get(_id))[0]

def search(self, **params):
return self._paginate_search(**params)

def _get_base_search_domain(self):
return [("backend_id", "=", self.shopinvader_backend.id)]

# The following method are 'private' and should be never never NEVER call
# from the controller.
# All params are trusted as they have been checked before

def _get(self, _id):
return self.env[self._expose_model].browse(_id)

def _to_json(self, records):
# TODO: not super-efficient w/ many records
# only if used w/ `shopinvader_data_stored`
return [rec.get_shop_data() for rec in records]

# Validator
def _validator_get(self):
return {}

def _validator_search(self):
validator = self._default_validator_search()
return validator
18 changes: 18 additions & 0 deletions shopfloor_shopinvader_base/views/shopfloor_app.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="shopfloor_app_form_view" model="ir.ui.view">
<field name="name">shopfloor app form</field>
<field name="model">shopfloor.app</field>
<field name="inherit_id" ref="shopfloor_base.shopfloor_app_form_view" />
<field name="arch" type="xml">
<page name="main" position="after">
<page name="shopinvader" string="Shopinvader">
<group name="shop_main">
<field name="shopinvader_backend_id" />
<field name="shopinvader_tech_user_id" />
</group>
</page>
</page>
</field>
</record>
</odoo>
1 change: 1 addition & 0 deletions shopfloor_shopinvader_mobile_base/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
wait for the bot ;)
1 change: 1 addition & 0 deletions shopfloor_shopinvader_mobile_base/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import controllers
Loading