From 7d5c797cc3c7eb5f832727818bc9aad73267fd3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Mon, 24 Jun 2019 18:27:47 +0200 Subject: [PATCH 01/18] [IMP] start adding module for dropoff site --- shopinvader_dropoff_site/__init__.py | 3 + shopinvader_dropoff_site/__manifest__.py | 21 ++++ shopinvader_dropoff_site/services/__init__.py | 3 + shopinvader_dropoff_site/services/cart.py | 97 +++++++++++++++++++ shopinvader_dropoff_site/tests/__init__.py | 3 + .../tests/test_dropoff_site.py | 67 +++++++++++++ 6 files changed, 194 insertions(+) create mode 100644 shopinvader_dropoff_site/__init__.py create mode 100644 shopinvader_dropoff_site/__manifest__.py create mode 100644 shopinvader_dropoff_site/services/__init__.py create mode 100644 shopinvader_dropoff_site/services/cart.py create mode 100644 shopinvader_dropoff_site/tests/__init__.py create mode 100644 shopinvader_dropoff_site/tests/test_dropoff_site.py diff --git a/shopinvader_dropoff_site/__init__.py b/shopinvader_dropoff_site/__init__.py new file mode 100644 index 0000000..54b9afb --- /dev/null +++ b/shopinvader_dropoff_site/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import services diff --git a/shopinvader_dropoff_site/__manifest__.py b/shopinvader_dropoff_site/__manifest__.py new file mode 100644 index 0000000..b2ea578 --- /dev/null +++ b/shopinvader_dropoff_site/__manifest__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 Akretion (http://www.akretion.com) +# Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +{ + "name": "Shopinvader Dropoff site", + "summary": "Shopinvader Dropoff site", + "version": "10.0.0.0.0", + "category": "e-commerce", + "website": "https://akretion.com", + "author": "Akretion", + "license": "AGPL-3", + "application": False, + "installable": True, + "external_dependencies": {"python": [], "bin": []}, + "depends": ["shopinvader_delivery_carrier", "delivery_dropoff_site"], + "data": [], + "demo": [], + "qweb": [], +} diff --git a/shopinvader_dropoff_site/services/__init__.py b/shopinvader_dropoff_site/services/__init__.py new file mode 100644 index 0000000..34405c3 --- /dev/null +++ b/shopinvader_dropoff_site/services/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import cart diff --git a/shopinvader_dropoff_site/services/cart.py b/shopinvader_dropoff_site/services/cart.py new file mode 100644 index 0000000..ad79f03 --- /dev/null +++ b/shopinvader_dropoff_site/services/cart.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo.addons.component.core import Component +from odoo.exceptions import UserError +from odoo.tools.translate import _ + + +class CartService(Component): + _inherit = "shopinvader.cart.service" + + def apply_dropoff_site(self, **params): + """ + This service will apply the given dropoffsite to the current + cart + :param params: Dict containing dropoff site information + :return: + """ + cart = self._get() + if not cart: + raise UserError(_("There is not cart")) + else: + self._set_dropoff_site(cart, params) + return self._to_json(cart) + + # Validator + def _validator_apply_dropoff_site(self): + return { + "ref": {"type": "string", "required": True}, + "name": {"type": "string", "required": True}, + "street": {"type": "string"}, + "street2": {"type": "string"}, + "zip": {"type": "string", "required": True}, + "city": {"type": "string", "required": True}, + "phone": {"type": "string"}, + "state_code": {"type": "string"}, + "country_code": {"type": "string", "required": True}, + } + + def _prepare_dropoff_site_params(self, cart, dropoff_site): + country_code = dropoff_site.pop("country_code") + state_code = dropoff_site.pop("state_code", None) + + country = self.env["res.country"].search([("code", "=", country_code)]) + if not country: + raise UserError(_("Invalid country code %s") % country_code) + dropoff_site["country_id"] = country.id + + if state_code: + state = self.env["res.country.state"].search( + [("code", "=", state_code), ("country_id", "=", country.id)] + ) + if not state: + raise UserError( + _("Invalid state code %s for country %s") + % (state_code, country_code) + ) + dropoff_site["state_id"] = state.id + + if not cart.carrier_id: + raise UserError(_("You must select a carrier first")) + dropoff_site["carrier_id"] = cart.carrier_id.id + return dropoff_site + + def _set_dropoff_site(self, cart, dropoff_site): + vals = self._prepare_dropoff_site_params(cart, dropoff_site) + dropoff_site_obj = self.env["dropoff.site"] + dropoff_site = dropoff_site_obj.search( + [ + ("carrier_id", "=", cart.carrier_id.id), + ("ref", "=", dropoff_site["ref"]), + ] + ) + if dropoff_site: + dropoff_site.write(vals) + else: + dropoff_site = dropoff_site_obj.create(vals) + vals = {"partner_shipping_id": dropoff_site.partner_id.id} + if not cart.final_shipping_partner_id: + vals["final_shipping_partner_id"] = cart.partner_shipping_id.id + cart.write(vals) + + def _reset_dropoff_site(self, cart): + if cart.final_shipping_partner_id: + cart.partner_shipping_id = cart.final_shipping_partner_id + cart.final_shipping_partner_id = None + + def _set_carrier(self, cart, carrier_id): + self._reset_dropoff_site(cart) + return super(CartService, self)._set_carrier(cart, carrier_id) + + def _unset_carrier(self, cart): + self._reset_dropoff_site(cart) + return super(CartService, self)._unset_carrier(cart) diff --git a/shopinvader_dropoff_site/tests/__init__.py b/shopinvader_dropoff_site/tests/__init__.py new file mode 100644 index 0000000..d59a501 --- /dev/null +++ b/shopinvader_dropoff_site/tests/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import test_dropoff_site diff --git a/shopinvader_dropoff_site/tests/test_dropoff_site.py b/shopinvader_dropoff_site/tests/test_dropoff_site.py new file mode 100644 index 0000000..c267f0b --- /dev/null +++ b/shopinvader_dropoff_site/tests/test_dropoff_site.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.addons.shopinvader_delivery_carrier.tests.test_carrier import ( + CommonCarrierCase, +) + + +class DropOffSiteCase(CommonCarrierCase): + def setUp(self): + super(DropOffSiteCase, self).setUp() + self.final_partner = self.cart.partner_shipping_id + self.poste_carrier.with_dropoff_site = True + self._set_carrier(self.poste_carrier) + self._set_dropoff_site(ref="foo", name="Bar") + + def _set_dropoff_site(self, ref, name): + self.service.dispatch( + "apply_dropoff_site", + params={ + "ref": ref, + "name": name, + "street": u"Boulevard Shopinvader", + "zip": u"69004", + "city": u"Lyon", + "country_code": u"FR", + }, + ) + + def test_setting_dropoff_site(self): + shipping = self.cart.partner_shipping_id + self.assertEqual(shipping.ref, "foo") + self.assertEqual(shipping.name, "Bar") + self.assertEqual( + self.cart.final_shipping_partner_id, self.final_partner + ) + + def test_updating_dropoff_site(self): + shipping = self.cart.partner_shipping_id + self._set_dropoff_site(ref="foo", name="Updated") + self.assertEqual(self.cart.partner_shipping_id, shipping) + self.assertEqual(shipping.ref, "foo") + self.assertEqual(shipping.name, "Updated") + self.assertEqual( + self.cart.final_shipping_partner_id, self.final_partner + ) + + def test_changing_dropoff_site(self): + previous_shipping = self.cart.partner_shipping_id + self._set_dropoff_site(ref="foo2", name="Bar2") + self.assertNotEqual(self.cart.partner_shipping_id, previous_shipping) + shipping = self.cart.partner_shipping_id + self.assertEqual(shipping.ref, "foo2") + self.assertEqual(shipping.name, "Bar2") + self.assertEqual( + self.cart.final_shipping_partner_id, self.final_partner + ) + + def test_change_carrier(self): + self._set_carrier(self.free_carrier) + self.assertEqual(self.cart.partner_shipping_id, self.final_partner) + + def test_unset_carrier(self): + self.service._unset_carrier(self.cart) + self.assertEqual(self.cart.partner_shipping_id, self.final_partner) From 0c63cdb66ef60ee6919798e0cfd75808e9cc7721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Fri, 28 Jun 2019 15:36:26 +0200 Subject: [PATCH 02/18] [REF] refactor code split edition part in a separated module and add laposte support --- .../readme/CONTRIBUTORS.rst | 1 + shopinvader_dropoff_site/readme/CREDITS.rst | 3 ++ .../readme/DESCRIPTION.rst | 6 +++ shopinvader_dropoff_site/readme/ROADMAP.rst | 4 ++ shopinvader_dropoff_site/services/cart.py | 54 +++---------------- .../tests/test_dropoff_site.py | 42 +++++---------- 6 files changed, 36 insertions(+), 74 deletions(-) create mode 100644 shopinvader_dropoff_site/readme/CONTRIBUTORS.rst create mode 100644 shopinvader_dropoff_site/readme/CREDITS.rst create mode 100644 shopinvader_dropoff_site/readme/DESCRIPTION.rst create mode 100644 shopinvader_dropoff_site/readme/ROADMAP.rst diff --git a/shopinvader_dropoff_site/readme/CONTRIBUTORS.rst b/shopinvader_dropoff_site/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000..4be8cbf --- /dev/null +++ b/shopinvader_dropoff_site/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Sebastien BEAU diff --git a/shopinvader_dropoff_site/readme/CREDITS.rst b/shopinvader_dropoff_site/readme/CREDITS.rst new file mode 100644 index 0000000..fdc8ca1 --- /dev/null +++ b/shopinvader_dropoff_site/readme/CREDITS.rst @@ -0,0 +1,3 @@ +The development of this module has been financially supported by: + +* Akretion diff --git a/shopinvader_dropoff_site/readme/DESCRIPTION.rst b/shopinvader_dropoff_site/readme/DESCRIPTION.rst new file mode 100644 index 0000000..72ae26e --- /dev/null +++ b/shopinvader_dropoff_site/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +This is shopinvader the odoo module for the new generation of e-commerce. + +This module is a base module for supporting dropoffsite + + +.. _Shopinvader: https://shopinvader.com diff --git a/shopinvader_dropoff_site/readme/ROADMAP.rst b/shopinvader_dropoff_site/readme/ROADMAP.rst new file mode 100644 index 0000000..518c7b7 --- /dev/null +++ b/shopinvader_dropoff_site/readme/ROADMAP.rst @@ -0,0 +1,4 @@ +For now the module only implement an api rest for setting the dropoffsite. + +TODO: +- add the possibility to search a dropoff_site with geo location diff --git a/shopinvader_dropoff_site/services/cart.py b/shopinvader_dropoff_site/services/cart.py index ad79f03..2767bcb 100644 --- a/shopinvader_dropoff_site/services/cart.py +++ b/shopinvader_dropoff_site/services/cart.py @@ -12,7 +12,7 @@ class CartService(Component): _inherit = "shopinvader.cart.service" - def apply_dropoff_site(self, **params): + def set_dropoff_site(self, **params): """ This service will apply the given dropoffsite to the current cart @@ -23,61 +23,23 @@ def apply_dropoff_site(self, **params): if not cart: raise UserError(_("There is not cart")) else: - self._set_dropoff_site(cart, params) + self._set_dropoff_site(cart, params["code"]) return self._to_json(cart) # Validator - def _validator_apply_dropoff_site(self): - return { - "ref": {"type": "string", "required": True}, - "name": {"type": "string", "required": True}, - "street": {"type": "string"}, - "street2": {"type": "string"}, - "zip": {"type": "string", "required": True}, - "city": {"type": "string", "required": True}, - "phone": {"type": "string"}, - "state_code": {"type": "string"}, - "country_code": {"type": "string", "required": True}, - } + def _validator_set_dropoff_site(self): + return {"code": {"type": "string", "required": True}} - def _prepare_dropoff_site_params(self, cart, dropoff_site): - country_code = dropoff_site.pop("country_code") - state_code = dropoff_site.pop("state_code", None) - - country = self.env["res.country"].search([("code", "=", country_code)]) - if not country: - raise UserError(_("Invalid country code %s") % country_code) - dropoff_site["country_id"] = country.id - - if state_code: - state = self.env["res.country.state"].search( - [("code", "=", state_code), ("country_id", "=", country.id)] - ) - if not state: - raise UserError( - _("Invalid state code %s for country %s") - % (state_code, country_code) - ) - dropoff_site["state_id"] = state.id - - if not cart.carrier_id: - raise UserError(_("You must select a carrier first")) - dropoff_site["carrier_id"] = cart.carrier_id.id - return dropoff_site - - def _set_dropoff_site(self, cart, dropoff_site): - vals = self._prepare_dropoff_site_params(cart, dropoff_site) + def _set_dropoff_site(self, cart, dropoff_site_code): dropoff_site_obj = self.env["dropoff.site"] dropoff_site = dropoff_site_obj.search( [ ("carrier_id", "=", cart.carrier_id.id), - ("ref", "=", dropoff_site["ref"]), + ("ref", "=", dropoff_site_code), ] ) - if dropoff_site: - dropoff_site.write(vals) - else: - dropoff_site = dropoff_site_obj.create(vals) + if not dropoff_site: + raise UserError(_("Invalid code for Dropoff site")) vals = {"partner_shipping_id": dropoff_site.partner_id.id} if not cart.final_shipping_partner_id: vals["final_shipping_partner_id"] = cart.partner_shipping_id.id diff --git a/shopinvader_dropoff_site/tests/test_dropoff_site.py b/shopinvader_dropoff_site/tests/test_dropoff_site.py index c267f0b..157c7f1 100644 --- a/shopinvader_dropoff_site/tests/test_dropoff_site.py +++ b/shopinvader_dropoff_site/tests/test_dropoff_site.py @@ -3,7 +3,7 @@ # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo.addons.shopinvader_delivery_carrier.tests.test_carrier import ( +from odoo.addons.shopinvader_delivery_carrier.tests.common import ( CommonCarrierCase, ) @@ -14,46 +14,32 @@ def setUp(self): self.final_partner = self.cart.partner_shipping_id self.poste_carrier.with_dropoff_site = True self._set_carrier(self.poste_carrier) - self._set_dropoff_site(ref="foo", name="Bar") - - def _set_dropoff_site(self, ref, name): - self.service.dispatch( - "apply_dropoff_site", - params={ - "ref": ref, - "name": name, - "street": u"Boulevard Shopinvader", - "zip": u"69004", - "city": u"Lyon", - "country_code": u"FR", - }, + self.dropoff_site_foo = self.env["dropoff.site"].create( + {"ref": "foo", "name": "Foo", "carrier_id": self.poste_carrier.id} ) - - def test_setting_dropoff_site(self): - shipping = self.cart.partner_shipping_id - self.assertEqual(shipping.ref, "foo") - self.assertEqual(shipping.name, "Bar") - self.assertEqual( - self.cart.final_shipping_partner_id, self.final_partner + self.dropoff_site_bar = self.env["dropoff.site"].create( + {"ref": "bar", "name": "Bar", "carrier_id": self.poste_carrier.id} ) + self._set_dropoff_site("foo") + + def _set_dropoff_site(self, code): + self.service.dispatch("set_dropoff_site", params={"code": code}) - def test_updating_dropoff_site(self): + def test_setting_dropoff_site(self): shipping = self.cart.partner_shipping_id - self._set_dropoff_site(ref="foo", name="Updated") - self.assertEqual(self.cart.partner_shipping_id, shipping) self.assertEqual(shipping.ref, "foo") - self.assertEqual(shipping.name, "Updated") + self.assertEqual(shipping.name, "Foo") self.assertEqual( self.cart.final_shipping_partner_id, self.final_partner ) def test_changing_dropoff_site(self): previous_shipping = self.cart.partner_shipping_id - self._set_dropoff_site(ref="foo2", name="Bar2") + self._set_dropoff_site("bar") self.assertNotEqual(self.cart.partner_shipping_id, previous_shipping) shipping = self.cart.partner_shipping_id - self.assertEqual(shipping.ref, "foo2") - self.assertEqual(shipping.name, "Bar2") + self.assertEqual(shipping.ref, "bar") + self.assertEqual(shipping.name, "Bar") self.assertEqual( self.cart.final_shipping_partner_id, self.final_partner ) From d902bb3d5c093d3589d9c940a93c34e808db9dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Mon, 1 Jul 2019 18:39:45 +0200 Subject: [PATCH 03/18] [IMP] add a flag to know if the shipping address is a dropoff site and add the name of the recipient --- shopinvader_dropoff_site/services/__init__.py | 1 + .../services/abstract_sale.py | 19 +++++++++++++++++++ .../tests/test_dropoff_site.py | 13 +++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 shopinvader_dropoff_site/services/abstract_sale.py diff --git a/shopinvader_dropoff_site/services/__init__.py b/shopinvader_dropoff_site/services/__init__.py index 34405c3..708edf5 100644 --- a/shopinvader_dropoff_site/services/__init__.py +++ b/shopinvader_dropoff_site/services/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- from . import cart +from . import abstract_sale diff --git a/shopinvader_dropoff_site/services/abstract_sale.py b/shopinvader_dropoff_site/services/abstract_sale.py new file mode 100644 index 0000000..fe42bd1 --- /dev/null +++ b/shopinvader_dropoff_site/services/abstract_sale.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Copyright 2016 Akretion (http://www.akretion.com) +# Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.addons.component.core import AbstractComponent + + +class AbstractSaleService(AbstractComponent): + _inherit = "shopinvader.abstract.sale.service" + + def _convert_shipping(self, sale): + res = super(AbstractSaleService, self)._convert_shipping(sale) + if sale.partner_shipping_id.is_dropoff_site: + res['address'].update({ + 'recipient_name': sale.final_shipping_partner_id.name, + 'is_dropoff_site': True, + }) + return res diff --git a/shopinvader_dropoff_site/tests/test_dropoff_site.py b/shopinvader_dropoff_site/tests/test_dropoff_site.py index 157c7f1..42580d3 100644 --- a/shopinvader_dropoff_site/tests/test_dropoff_site.py +++ b/shopinvader_dropoff_site/tests/test_dropoff_site.py @@ -23,7 +23,9 @@ def setUp(self): self._set_dropoff_site("foo") def _set_dropoff_site(self, code): - self.service.dispatch("set_dropoff_site", params={"code": code}) + self.res_cart = self.service.dispatch( + "set_dropoff_site", params={"code": code})['data'] + self.res_address = self.res_cart['shipping']['address'] def test_setting_dropoff_site(self): shipping = self.cart.partner_shipping_id @@ -32,6 +34,8 @@ def test_setting_dropoff_site(self): self.assertEqual( self.cart.final_shipping_partner_id, self.final_partner ) + self.assertEqual(self.res_address['is_dropoff_site'], True) + self.assertEqual(self.res_address['recipient_name'], 'Osiris') def test_changing_dropoff_site(self): previous_shipping = self.cart.partner_shipping_id @@ -43,10 +47,15 @@ def test_changing_dropoff_site(self): self.assertEqual( self.cart.final_shipping_partner_id, self.final_partner ) + self.assertEqual(self.res_address['is_dropoff_site'], True) + self.assertEqual(self.res_address['recipient_name'], 'Osiris') def test_change_carrier(self): - self._set_carrier(self.free_carrier) + cart = self._set_carrier(self.free_carrier) + address = cart['shipping']['address'] self.assertEqual(self.cart.partner_shipping_id, self.final_partner) + self.assertNotIn('is_dropoff_site', address) + self.assertNotIn('recipient_name', address) def test_unset_carrier(self): self.service._unset_carrier(self.cart) From 5020150248ed315b35d3d658f07b7b8f542de3a0 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Sat, 3 Aug 2019 15:08:13 +0200 Subject: [PATCH 04/18] [IMP] rename shopinvader_dropoff_site into shopinvader_delivery_pickup --- .../__init__.py | 0 .../__manifest__.py | 4 ++-- .../readme/CONTRIBUTORS.rst | 0 .../readme/CREDITS.rst | 0 .../readme/DESCRIPTION.rst | 0 .../readme/ROADMAP.rst | 0 .../services/__init__.py | 0 .../services/abstract_sale.py | 0 .../services/cart.py | 0 .../tests/__init__.py | 0 .../tests/test_dropoff_site.py | 0 11 files changed, 2 insertions(+), 2 deletions(-) rename {shopinvader_dropoff_site => shopinvader_delivery_pickup}/__init__.py (100%) rename {shopinvader_dropoff_site => shopinvader_delivery_pickup}/__manifest__.py (86%) rename {shopinvader_dropoff_site => shopinvader_delivery_pickup}/readme/CONTRIBUTORS.rst (100%) rename {shopinvader_dropoff_site => shopinvader_delivery_pickup}/readme/CREDITS.rst (100%) rename {shopinvader_dropoff_site => shopinvader_delivery_pickup}/readme/DESCRIPTION.rst (100%) rename {shopinvader_dropoff_site => shopinvader_delivery_pickup}/readme/ROADMAP.rst (100%) rename {shopinvader_dropoff_site => shopinvader_delivery_pickup}/services/__init__.py (100%) rename {shopinvader_dropoff_site => shopinvader_delivery_pickup}/services/abstract_sale.py (100%) rename {shopinvader_dropoff_site => shopinvader_delivery_pickup}/services/cart.py (100%) rename {shopinvader_dropoff_site => shopinvader_delivery_pickup}/tests/__init__.py (100%) rename {shopinvader_dropoff_site => shopinvader_delivery_pickup}/tests/test_dropoff_site.py (100%) diff --git a/shopinvader_dropoff_site/__init__.py b/shopinvader_delivery_pickup/__init__.py similarity index 100% rename from shopinvader_dropoff_site/__init__.py rename to shopinvader_delivery_pickup/__init__.py diff --git a/shopinvader_dropoff_site/__manifest__.py b/shopinvader_delivery_pickup/__manifest__.py similarity index 86% rename from shopinvader_dropoff_site/__manifest__.py rename to shopinvader_delivery_pickup/__manifest__.py index b2ea578..28d0001 100644 --- a/shopinvader_dropoff_site/__manifest__.py +++ b/shopinvader_delivery_pickup/__manifest__.py @@ -4,8 +4,8 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { - "name": "Shopinvader Dropoff site", - "summary": "Shopinvader Dropoff site", + "name": "Shopinvader Delivery pickup", + "summary": "Shopinvader Delivery pickupt", "version": "10.0.0.0.0", "category": "e-commerce", "website": "https://akretion.com", diff --git a/shopinvader_dropoff_site/readme/CONTRIBUTORS.rst b/shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst similarity index 100% rename from shopinvader_dropoff_site/readme/CONTRIBUTORS.rst rename to shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst diff --git a/shopinvader_dropoff_site/readme/CREDITS.rst b/shopinvader_delivery_pickup/readme/CREDITS.rst similarity index 100% rename from shopinvader_dropoff_site/readme/CREDITS.rst rename to shopinvader_delivery_pickup/readme/CREDITS.rst diff --git a/shopinvader_dropoff_site/readme/DESCRIPTION.rst b/shopinvader_delivery_pickup/readme/DESCRIPTION.rst similarity index 100% rename from shopinvader_dropoff_site/readme/DESCRIPTION.rst rename to shopinvader_delivery_pickup/readme/DESCRIPTION.rst diff --git a/shopinvader_dropoff_site/readme/ROADMAP.rst b/shopinvader_delivery_pickup/readme/ROADMAP.rst similarity index 100% rename from shopinvader_dropoff_site/readme/ROADMAP.rst rename to shopinvader_delivery_pickup/readme/ROADMAP.rst diff --git a/shopinvader_dropoff_site/services/__init__.py b/shopinvader_delivery_pickup/services/__init__.py similarity index 100% rename from shopinvader_dropoff_site/services/__init__.py rename to shopinvader_delivery_pickup/services/__init__.py diff --git a/shopinvader_dropoff_site/services/abstract_sale.py b/shopinvader_delivery_pickup/services/abstract_sale.py similarity index 100% rename from shopinvader_dropoff_site/services/abstract_sale.py rename to shopinvader_delivery_pickup/services/abstract_sale.py diff --git a/shopinvader_dropoff_site/services/cart.py b/shopinvader_delivery_pickup/services/cart.py similarity index 100% rename from shopinvader_dropoff_site/services/cart.py rename to shopinvader_delivery_pickup/services/cart.py diff --git a/shopinvader_dropoff_site/tests/__init__.py b/shopinvader_delivery_pickup/tests/__init__.py similarity index 100% rename from shopinvader_dropoff_site/tests/__init__.py rename to shopinvader_delivery_pickup/tests/__init__.py diff --git a/shopinvader_dropoff_site/tests/test_dropoff_site.py b/shopinvader_delivery_pickup/tests/test_dropoff_site.py similarity index 100% rename from shopinvader_dropoff_site/tests/test_dropoff_site.py rename to shopinvader_delivery_pickup/tests/test_dropoff_site.py From ed63fdd34597585822a9eb1da979d93aef50cc1e Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Sat, 3 Aug 2019 15:55:11 +0200 Subject: [PATCH 05/18] [IMP] shopinvader_delivery_pickup: rename set_dropoff_site into set_delivery_pickup In the same time, when setting the pickup site, the the set_delivry method is not set on the sale order --- shopinvader_delivery_pickup/__manifest__.py | 6 +- .../services/abstract_sale.py | 10 +-- shopinvader_delivery_pickup/services/cart.py | 47 +++++++------- shopinvader_delivery_pickup/tests/__init__.py | 2 +- .../tests/test_cart.py | 65 +++++++++++++++++++ 5 files changed, 99 insertions(+), 31 deletions(-) create mode 100644 shopinvader_delivery_pickup/tests/test_cart.py diff --git a/shopinvader_delivery_pickup/__manifest__.py b/shopinvader_delivery_pickup/__manifest__.py index 28d0001..febb566 100644 --- a/shopinvader_delivery_pickup/__manifest__.py +++ b/shopinvader_delivery_pickup/__manifest__.py @@ -5,11 +5,11 @@ { "name": "Shopinvader Delivery pickup", - "summary": "Shopinvader Delivery pickupt", + "summary": "Allows to deliver sale order to pickup site", "version": "10.0.0.0.0", "category": "e-commerce", - "website": "https://akretion.com", - "author": "Akretion", + "website": "https://github.com/shopinvader/odoo-shopinvader-carrier", + "author": "Akretion, ACSONE SA/NV", "license": "AGPL-3", "application": False, "installable": True, diff --git a/shopinvader_delivery_pickup/services/abstract_sale.py b/shopinvader_delivery_pickup/services/abstract_sale.py index fe42bd1..a50e3be 100644 --- a/shopinvader_delivery_pickup/services/abstract_sale.py +++ b/shopinvader_delivery_pickup/services/abstract_sale.py @@ -12,8 +12,10 @@ class AbstractSaleService(AbstractComponent): def _convert_shipping(self, sale): res = super(AbstractSaleService, self)._convert_shipping(sale) if sale.partner_shipping_id.is_dropoff_site: - res['address'].update({ - 'recipient_name': sale.final_shipping_partner_id.name, - 'is_dropoff_site': True, - }) + res["address"].update( + { + "recipient_name": sale.final_shipping_partner_id.name, + "is_pickup_site": True, + } + ) return res diff --git a/shopinvader_delivery_pickup/services/cart.py b/shopinvader_delivery_pickup/services/cart.py index 2767bcb..9414323 100644 --- a/shopinvader_delivery_pickup/services/cart.py +++ b/shopinvader_delivery_pickup/services/cart.py @@ -3,7 +3,7 @@ # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - +from odoo.addons.base_rest.components.service import to_int from odoo.addons.component.core import Component from odoo.exceptions import UserError from odoo.tools.translate import _ @@ -12,48 +12,49 @@ class CartService(Component): _inherit = "shopinvader.cart.service" - def set_dropoff_site(self, **params): + # Public services + + def set_delivery_pickup(self, **params): """ - This service will apply the given dropoffsite to the current - cart - :param params: Dict containing dropoff site information + This service will apply the given pickup site AND the linked + carrier to the current cart + :param params: Dict containing pickup site information :return: """ cart = self._get() if not cart: raise UserError(_("There is not cart")) else: - self._set_dropoff_site(cart, params["code"]) + self._set_delivery_pickup(cart, params["pickup_site_id"]) return self._to_json(cart) # Validator - def _validator_set_dropoff_site(self): - return {"code": {"type": "string", "required": True}} - - def _set_dropoff_site(self, cart, dropoff_site_code): - dropoff_site_obj = self.env["dropoff.site"] - dropoff_site = dropoff_site_obj.search( - [ - ("carrier_id", "=", cart.carrier_id.id), - ("ref", "=", dropoff_site_code), - ] - ) - if not dropoff_site: - raise UserError(_("Invalid code for Dropoff site")) - vals = {"partner_shipping_id": dropoff_site.partner_id.id} + + def _validator_set_delivery_pickup(self): + return {"pickup_site_id": {"coerce": to_int}} + + # Services implementation + + def _set_delivery_pickup(self, cart, pickup_site_id): + pickup_site_obj = self.env["dropoff.site"] + pickup_site = pickup_site_obj.search([("id", "=", pickup_site_id)]) + if not pickup_site: + raise UserError(_("Invalid code for pickup site")) + self._set_carrier(cart, pickup_site.carrier_id.id) + vals = {"partner_shipping_id": pickup_site.partner_id.id} if not cart.final_shipping_partner_id: vals["final_shipping_partner_id"] = cart.partner_shipping_id.id cart.write(vals) - def _reset_dropoff_site(self, cart): + def _reset_delivery_pickup(self, cart): if cart.final_shipping_partner_id: cart.partner_shipping_id = cart.final_shipping_partner_id cart.final_shipping_partner_id = None def _set_carrier(self, cart, carrier_id): - self._reset_dropoff_site(cart) + self._reset_delivery_pickup(cart) return super(CartService, self)._set_carrier(cart, carrier_id) def _unset_carrier(self, cart): - self._reset_dropoff_site(cart) + self._reset_delivery_pickup(cart) return super(CartService, self)._unset_carrier(cart) diff --git a/shopinvader_delivery_pickup/tests/__init__.py b/shopinvader_delivery_pickup/tests/__init__.py index d59a501..9585302 100644 --- a/shopinvader_delivery_pickup/tests/__init__.py +++ b/shopinvader_delivery_pickup/tests/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -from . import test_dropoff_site +from . import test_cart diff --git a/shopinvader_delivery_pickup/tests/test_cart.py b/shopinvader_delivery_pickup/tests/test_cart.py new file mode 100644 index 0000000..1b5f5a1 --- /dev/null +++ b/shopinvader_delivery_pickup/tests/test_cart.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp.addons.shopinvader_delivery_carrier.tests.common import ( + CommonCarrierCase, +) + + +class TestCart(CommonCarrierCase): + def setUp(self): + super(TestCart, self).setUp() + self.final_partner = self.cart.partner_shipping_id + self.poste_carrier.with_pickup_site = True + self._set_carrier(self.poste_carrier) + self.pickup_site_foo = self.env["dropoff.site"].create( + {"ref": "foo", "name": "Foo", "carrier_id": self.poste_carrier.id} + ) + self.pickup_site_bar = self.env["dropoff.site"].create( + {"ref": "bar", "name": "Bar", "carrier_id": self.free_carrier.id} + ) + self._set_delivery_pickup(self.pickup_site_foo.id) + + def _set_delivery_pickup(self, pickup_site_id): + self.res_cart = self.service.dispatch( + "set_delivery_pickup", params={"pickup_site_id": pickup_site_id} + )["data"] + self.res_address = self.res_cart["shipping"]["address"] + + def test_setting_pickup_site(self): + shipping = self.cart.partner_shipping_id + self.assertEqual(shipping.ref, "foo") + self.assertEqual(shipping.name, "Foo") + self.assertEqual( + self.cart.final_shipping_partner_id, self.final_partner + ) + self.assertEqual(self.res_address["is_pickup_site"], True) + self.assertEqual(self.res_address["recipient_name"], "Osiris") + self.assertEqual(self.cart.carrier_id, self.poste_carrier) + + def test_changing_pickup_site(self): + previous_shipping = self.cart.partner_shipping_id + self._set_delivery_pickup(self.pickup_site_bar.id) + self.assertNotEqual(self.cart.partner_shipping_id, previous_shipping) + shipping = self.cart.partner_shipping_id + self.assertEqual(shipping.ref, "bar") + self.assertEqual(shipping.name, "Bar") + self.assertEqual( + self.cart.final_shipping_partner_id, self.final_partner + ) + self.assertEqual(self.res_address["is_pickup_site"], True) + self.assertEqual(self.res_address["recipient_name"], "Osiris") + self.assertEqual(self.cart.carrier_id, self.free_carrier) + + def test_change_carrier(self): + cart = self._set_carrier(self.free_carrier) + address = cart["shipping"]["address"] + self.assertEqual(self.cart.partner_shipping_id, self.final_partner) + self.assertNotIn("is_pickup_site", address) + self.assertNotIn("recipient_name", address) + + def test_unset_carrier(self): + self.service._unset_carrier(self.cart) + self.assertEqual(self.cart.partner_shipping_id, self.final_partner) From 2223066964ce20c13acb77d43ccacbe1f4032825 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Sat, 3 Aug 2019 20:36:16 +0200 Subject: [PATCH 06/18] [IMP] shopinvader_delivery_pickup: Add delivery_pickup service This service can be used to search for pickup sites available for the site --- .../services/__init__.py | 4 +- .../services/delivery_carrier.py | 23 +++ .../services/delivery_pickup.py | 147 ++++++++++++++++++ shopinvader_delivery_pickup/tests/__init__.py | 4 +- shopinvader_delivery_pickup/tests/common.py | 37 +++++ .../tests/test_cart.py | 27 +--- .../tests/test_delivery_carrier.py | 39 +++++ .../tests/test_delivery_pickup.py | 97 ++++++++++++ 8 files changed, 351 insertions(+), 27 deletions(-) create mode 100644 shopinvader_delivery_pickup/services/delivery_carrier.py create mode 100644 shopinvader_delivery_pickup/services/delivery_pickup.py create mode 100644 shopinvader_delivery_pickup/tests/common.py create mode 100644 shopinvader_delivery_pickup/tests/test_delivery_carrier.py create mode 100644 shopinvader_delivery_pickup/tests/test_delivery_pickup.py diff --git a/shopinvader_delivery_pickup/services/__init__.py b/shopinvader_delivery_pickup/services/__init__.py index 708edf5..f578e20 100644 --- a/shopinvader_delivery_pickup/services/__init__.py +++ b/shopinvader_delivery_pickup/services/__init__.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- -from . import cart from . import abstract_sale +from . import cart +from . import delivery_pickup +from . import delivery_carrier diff --git a/shopinvader_delivery_pickup/services/delivery_carrier.py b/shopinvader_delivery_pickup/services/delivery_carrier.py new file mode 100644 index 0000000..de719dd --- /dev/null +++ b/shopinvader_delivery_pickup/services/delivery_carrier.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# pylint: disable=consider-merging-classes-inherited,method-required-super + +from openerp.addons.component.core import Component + + +class DeliveryCarrierService(Component): + _inherit = "shopinvader.delivery.carrier.service" + + @property + def allowed_carrier_types(self): + res = super(DeliveryCarrierService, self).allowed_carrier_types + res.append("pickup") + return res + + def _prepare_carrier(self, carrier): + res = super(DeliveryCarrierService, self)._prepare_carrier(carrier) + if carrier.with_dropoff_site: + res["type"] = "pickup" + return res diff --git a/shopinvader_delivery_pickup/services/delivery_pickup.py b/shopinvader_delivery_pickup/services/delivery_pickup.py new file mode 100644 index 0000000..44acdf5 --- /dev/null +++ b/shopinvader_delivery_pickup/services/delivery_pickup.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp.addons.base_rest.components.service import to_int +from openerp.addons.component.core import Component +from openerp.osv.expression import FALSE_DOMAIN + + +class DeliveryPickupService(Component): + _inherit = "base.shopinvader.service" + _name = "shopinvader.delivery.pickup.service" + _usage = "delivery_pickup" + _description = """ + This service allows you to retrieve the information of available + pickup sites. + """ + + # Public services: + + def search(self, **params): + """ + Returns the list of available pickup sitecs + + If the target params == current_cart, the list will be limited to the + pickup sites linked to carriers applying to the current cart. + + If you don't provide a carrier_id, the service will return all the + pickup sites linked to carriers available for this site. + + If you provide a carrier_id, only the pickup sites linked to the given + carrier are returned except if the carrier is not available for this + site. + + """ + dropoff_sites = self._search(**params) + return { + "count": len(dropoff_sites), + "rows": [self._dropoff_site_to_json(ds) for ds in dropoff_sites], + } + + # Validators + + def _validator_search(self): + return { + "target": { + "type": "string", + "required": False, + "allowed": ["current_cart"], + }, + "carrier_id": { + "type": "integer", + "coerce": to_int, + "required": False, + "nullable": False, + }, + } + + def _validator_return_search(self): + return { + "count": {"type": "integer", "required": True}, + "rows": { + "type": "list", + "required": True, + "schema": { + "type": "dict", + "schema": self._pickup_site_schema(), + }, + }, + } + + def _pickup_site_schema(self): + return { + "id": {"type": "integer", "required": True}, + "name": {"type": "string", "required": True}, + "ref": {"type": "string", "nullable": True}, + "street": {"type": "string", "nullable": True}, + "street2": {"type": "string", "nullable": True}, + "zip": {"type": "string", "nullable": True}, + "city": {"type": "string", "nullable": True}, + "phone": {"type": "string", "nullable": True}, + "state": { + "type": "dict", + "nullable": True, + "schema": { + "id": {"type": "integer", "required": True}, + "name": {"type": "string", "required": True}, + }, + }, + "country": { + "type": "dict", + "nullable": True, + "schema": { + "id": {"type": "integer", "required": True}, + "name": {"type": "string", "required": True}, + }, + }, + "carrier": { + "type": "dict", + "nullable": True, + "schema": { + "id": {"type": "integer", "required": True}, + "name": {"type": "string", "required": True}, + }, + }, + } + + # Services implementation + + def _search(self, **params): + """ + Search for delively carriers + :param params: see _validator_search + :return: a list of delivery.carriers + """ + domain = self._search_param_to_domain(**params) + return self.env["dropoff.site"].search(domain) + + def _search_param_to_domain(self, **params): + # first of all, always restrict dropoff site for available carrier + available_carriers = self.component(usage="delivery_carrier")._search( + target=params.get("target") + ) + carrier_id = params.get("carrier_id") + if carrier_id: + if carrier_id not in available_carriers.ids: + return FALSE_DOMAIN + return [("carrier_id", "=", carrier_id)] + return [("carrier_id", "in", available_carriers.ids)] + + def _dropoff_site_to_json(self, dropoff_site): + return dropoff_site.jsonify(self._json_parser())[0] + + def _json_parser(self): + return [ + "id", + "name", + "ref", + "street", + "street2", + "zip", + "city", + "phone", + ("state_id:state", ["id", "name"]), + ("country_id:country", ["id", "name"]), + ("carrier_id:carrier", ["id", "name"]), + ] diff --git a/shopinvader_delivery_pickup/tests/__init__.py b/shopinvader_delivery_pickup/tests/__init__.py index 9585302..c1dcb8e 100644 --- a/shopinvader_delivery_pickup/tests/__init__.py +++ b/shopinvader_delivery_pickup/tests/__init__.py @@ -1,3 +1,3 @@ -# -*- coding: utf-8 -*- - from . import test_cart +from . import test_delivery_pickup +from . import test_delivery_carrier diff --git a/shopinvader_delivery_pickup/tests/common.py b/shopinvader_delivery_pickup/tests/common.py new file mode 100644 index 0000000..a6d5e68 --- /dev/null +++ b/shopinvader_delivery_pickup/tests/common.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 Akretion (http://www.akretion.com). +# Copyright 2019 ACSONE SA/NV +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp.addons.shopinvader_delivery_carrier.tests.common import ( + CommonCarrierCase, +) + + +class CommondDeliveryPickupCase(CommonCarrierCase): + def setUp(self): + super(CommondDeliveryPickupCase, self).setUp() + self.final_partner = self.cart.partner_shipping_id + self.poste_carrier.with_dropoff_site = True + self._set_carrier(self.poste_carrier) + self.pickup_site_foo = self.env["dropoff.site"].create( + {"ref": "foo", "name": "Foo", "carrier_id": self.poste_carrier.id} + ) + self.pickup_site_bar = self.env["dropoff.site"].create( + {"ref": "bar", "name": "Bar", "carrier_id": self.free_carrier.id} + ) + self.cart_service = self.service + self.delivery_pickup_service = self.cart_service.component( + usage="delivery_pickup" + ) + self._cart_set_delivery_pickup(self.pickup_site_foo.id) + + def _cart_set_delivery_pickup(self, pickup_site_id): + self.res_cart = self.cart_service.dispatch( + "set_delivery_pickup", params={"pickup_site_id": pickup_site_id} + )["data"] + self.res_address = self.res_cart["shipping"]["address"] + + def _delivery_pickup_search(self, **params): + return self.delivery_pickup_service.dispatch("search", params=params) diff --git a/shopinvader_delivery_pickup/tests/test_cart.py b/shopinvader_delivery_pickup/tests/test_cart.py index 1b5f5a1..aa52903 100644 --- a/shopinvader_delivery_pickup/tests/test_cart.py +++ b/shopinvader_delivery_pickup/tests/test_cart.py @@ -3,31 +3,10 @@ # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp.addons.shopinvader_delivery_carrier.tests.common import ( - CommonCarrierCase, -) +from .common import CommondDeliveryPickupCase -class TestCart(CommonCarrierCase): - def setUp(self): - super(TestCart, self).setUp() - self.final_partner = self.cart.partner_shipping_id - self.poste_carrier.with_pickup_site = True - self._set_carrier(self.poste_carrier) - self.pickup_site_foo = self.env["dropoff.site"].create( - {"ref": "foo", "name": "Foo", "carrier_id": self.poste_carrier.id} - ) - self.pickup_site_bar = self.env["dropoff.site"].create( - {"ref": "bar", "name": "Bar", "carrier_id": self.free_carrier.id} - ) - self._set_delivery_pickup(self.pickup_site_foo.id) - - def _set_delivery_pickup(self, pickup_site_id): - self.res_cart = self.service.dispatch( - "set_delivery_pickup", params={"pickup_site_id": pickup_site_id} - )["data"] - self.res_address = self.res_cart["shipping"]["address"] - +class TestCart(CommondDeliveryPickupCase): def test_setting_pickup_site(self): shipping = self.cart.partner_shipping_id self.assertEqual(shipping.ref, "foo") @@ -41,7 +20,7 @@ def test_setting_pickup_site(self): def test_changing_pickup_site(self): previous_shipping = self.cart.partner_shipping_id - self._set_delivery_pickup(self.pickup_site_bar.id) + self._cart_set_delivery_pickup(self.pickup_site_bar.id) self.assertNotEqual(self.cart.partner_shipping_id, previous_shipping) shipping = self.cart.partner_shipping_id self.assertEqual(shipping.ref, "bar") diff --git a/shopinvader_delivery_pickup/tests/test_delivery_carrier.py b/shopinvader_delivery_pickup/tests/test_delivery_carrier.py new file mode 100644 index 0000000..7b97a03 --- /dev/null +++ b/shopinvader_delivery_pickup/tests/test_delivery_carrier.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from openerp.addons.shopinvader_delivery_carrier.tests.common import ( + CommonCarrierCase, +) + + +class TestDeliveryCarrier(CommonCarrierCase): + def setUp(self): + super(CommonCarrierCase, self).setUp() + self.carrier_service = self.service.component("delivery_carrier") + self.poste_carrier.with_dropoff_site = True + self.pickup_site_foo = self.env["dropoff.site"].create( + {"ref": "foo", "name": "Foo", "carrier_id": self.poste_carrier.id} + ) + + def test_search_all(self): + res = self.carrier_service.search() + expected = { + "count": 2, + "rows": [ + { + "price": 0.0, + "description": False, + "id": self.free_carrier.id, + "name": self.free_carrier.name, + "type": None, + }, + { + "price": 0.0, + "description": False, + "id": self.poste_carrier.id, + "name": self.poste_carrier.name, + "type": "pickup", + }, + ], + } + self.assertDictEqual(res, expected) diff --git a/shopinvader_delivery_pickup/tests/test_delivery_pickup.py b/shopinvader_delivery_pickup/tests/test_delivery_pickup.py new file mode 100644 index 0000000..836204a --- /dev/null +++ b/shopinvader_delivery_pickup/tests/test_delivery_pickup.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from .common import CommondDeliveryPickupCase + + +class TestDeliveryCarrier(CommondDeliveryPickupCase): + def _assertExpectedPickupSites(self, search_result, dropoff_sites): + self.assertIn("count", search_result) + self.assertEqual(search_result["count"], len(dropoff_sites)) + pickup_ids = [r["id"] for r in search_result["rows"]] + self.assertSetEqual(set(dropoff_sites.ids), set(pickup_ids)) + + def test_01(self): + """ + Data: + * a backend with 2 delivery methods (poste, free) + * 2 dropoff_site defined for delivery la poste + Test Case: + * search delivery_pickup without parameters + Expected result: + * 2 pickup site found + :return: + """ + res = self._delivery_pickup_search() + self._assertExpectedPickupSites( + res, self.pickup_site_foo | self.pickup_site_bar + ) + + def test_02(self): + """ + Data: + * a backend without delivery method + * 2 dropoff_site defined for delivery la poste + Test Case: + * search delivery_pickup without parameters + Expected result: + * No site found + :return: + """ + self.backend.write({"carrier_ids": [(5, None, None)]}) + res = self._delivery_pickup_search() + self._assertExpectedPickupSites(res, self.env["dropoff.site"].browse()) + + def test_03(self): + """ + Data: + * a backend without delivery method + * 2 dropoff_site defined for delivery la poste + Test Case: + * search delivery_pickup for carrier la poste + Expected result: + * No site found + :return: + """ + self.backend.write({"carrier_ids": [(5, None, None)]}) + res = self._delivery_pickup_search(carrier_id=self.poste_carrier.id) + self._assertExpectedPickupSites(res, self.env["dropoff.site"].browse()) + + def test_04(self): + """ + Data: + * a backend without 2 delivery methods (poste, free) + * 1 dropoff_site defined for delivery la poste + * 1 dropoff_site defined for delivery free + Test Case: + * search delivery_pickup for carrier la poste + Expected result: + * The result must contains the pickup site linked to poste + :return: + """ + self.pickup_site_bar.carrier_id = self.free_carrier + self.pickup_site_foo.carrier_id = self.poste_carrier + res = self._delivery_pickup_search(carrier_id=self.poste_carrier.id) + self._assertExpectedPickupSites(res, self.pickup_site_foo) + + def test_05(self): + """ + Data: + * a backend without 2 delivery methods (poste, free) + * 1 dropoff_site defined for delivery la poste + * 1 dropoff_site defined for delivery la free + Test Case: + * search delivery_pickup for target 'current_cart' + Expected result: + * The result must contains the 3 pickup sites since the 2 carriers + are available on the current cart + :return: + """ + self.pickup_site_bar.carrier_id = self.free_carrier + self.pickup_site_foo.carrier_id = self.poste_carrier + self._set_carrier(self.poste_carrier) + res = self._delivery_pickup_search(target="current_cart") + self._assertExpectedPickupSites( + res, self.pickup_site_foo | self.pickup_site_bar + ) From 3b90b2bfdd650a2dc6d68557ae8e8640e720a902 Mon Sep 17 00:00:00 2001 From: beau sebastien Date: Tue, 22 Oct 2019 14:54:09 +0200 Subject: [PATCH 07/18] Fix copyright due to cherry-pick Co-Authored-By: Laurent Mignon (ACSONE) --- shopinvader_delivery_pickup/readme/CREDITS.rst | 1 + shopinvader_delivery_pickup/services/delivery_carrier.py | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shopinvader_delivery_pickup/readme/CREDITS.rst b/shopinvader_delivery_pickup/readme/CREDITS.rst index fdc8ca1..ea05716 100644 --- a/shopinvader_delivery_pickup/readme/CREDITS.rst +++ b/shopinvader_delivery_pickup/readme/CREDITS.rst @@ -1,3 +1,4 @@ The development of this module has been financially supported by: * Akretion +* ACSONE SA/NV diff --git a/shopinvader_delivery_pickup/services/delivery_carrier.py b/shopinvader_delivery_pickup/services/delivery_carrier.py index de719dd..04dc264 100644 --- a/shopinvader_delivery_pickup/services/delivery_carrier.py +++ b/shopinvader_delivery_pickup/services/delivery_carrier.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright 2017 Akretion (http://www.akretion.com). -# @author Sébastien BEAU +# Copyright 2019 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # pylint: disable=consider-merging-classes-inherited,method-required-super From d16d0aa12d22ec3f8d879a560ea04b01e0be7fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Tue, 22 Oct 2019 17:07:54 +0200 Subject: [PATCH 08/18] [FIx] fix test and fix consistency of service name --- .../services/delivery_pickup.py | 4 +- shopinvader_delivery_pickup/tests/common.py | 2 +- .../tests/test_delivery_carrier.py | 6 +- .../tests/test_dropoff_site.py | 62 ------------------- 4 files changed, 6 insertions(+), 68 deletions(-) delete mode 100644 shopinvader_delivery_pickup/tests/test_dropoff_site.py diff --git a/shopinvader_delivery_pickup/services/delivery_pickup.py b/shopinvader_delivery_pickup/services/delivery_pickup.py index 44acdf5..8b9b0ad 100644 --- a/shopinvader_delivery_pickup/services/delivery_pickup.py +++ b/shopinvader_delivery_pickup/services/delivery_pickup.py @@ -10,7 +10,7 @@ class DeliveryPickupService(Component): _inherit = "base.shopinvader.service" _name = "shopinvader.delivery.pickup.service" - _usage = "delivery_pickup" + _usage = "delivery_pickups" _description = """ This service allows you to retrieve the information of available pickup sites. @@ -118,7 +118,7 @@ def _search(self, **params): def _search_param_to_domain(self, **params): # first of all, always restrict dropoff site for available carrier - available_carriers = self.component(usage="delivery_carrier")._search( + available_carriers = self.component(usage="delivery_carriers")._search( target=params.get("target") ) carrier_id = params.get("carrier_id") diff --git a/shopinvader_delivery_pickup/tests/common.py b/shopinvader_delivery_pickup/tests/common.py index a6d5e68..79ff29e 100644 --- a/shopinvader_delivery_pickup/tests/common.py +++ b/shopinvader_delivery_pickup/tests/common.py @@ -23,7 +23,7 @@ def setUp(self): ) self.cart_service = self.service self.delivery_pickup_service = self.cart_service.component( - usage="delivery_pickup" + usage="delivery_pickups" ) self._cart_set_delivery_pickup(self.pickup_site_foo.id) diff --git a/shopinvader_delivery_pickup/tests/test_delivery_carrier.py b/shopinvader_delivery_pickup/tests/test_delivery_carrier.py index 7b97a03..2500a27 100644 --- a/shopinvader_delivery_pickup/tests/test_delivery_carrier.py +++ b/shopinvader_delivery_pickup/tests/test_delivery_carrier.py @@ -9,7 +9,7 @@ class TestDeliveryCarrier(CommonCarrierCase): def setUp(self): super(CommonCarrierCase, self).setUp() - self.carrier_service = self.service.component("delivery_carrier") + self.carrier_service = self.service.component("delivery_carriers") self.poste_carrier.with_dropoff_site = True self.pickup_site_foo = self.env["dropoff.site"].create( {"ref": "foo", "name": "Foo", "carrier_id": self.poste_carrier.id} @@ -22,14 +22,14 @@ def test_search_all(self): "rows": [ { "price": 0.0, - "description": False, + "description": None, "id": self.free_carrier.id, "name": self.free_carrier.name, "type": None, }, { "price": 0.0, - "description": False, + "description": None, "id": self.poste_carrier.id, "name": self.poste_carrier.name, "type": "pickup", diff --git a/shopinvader_delivery_pickup/tests/test_dropoff_site.py b/shopinvader_delivery_pickup/tests/test_dropoff_site.py deleted file mode 100644 index 42580d3..0000000 --- a/shopinvader_delivery_pickup/tests/test_dropoff_site.py +++ /dev/null @@ -1,62 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright 2019 Akretion (http://www.akretion.com). -# @author Sébastien BEAU -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.addons.shopinvader_delivery_carrier.tests.common import ( - CommonCarrierCase, -) - - -class DropOffSiteCase(CommonCarrierCase): - def setUp(self): - super(DropOffSiteCase, self).setUp() - self.final_partner = self.cart.partner_shipping_id - self.poste_carrier.with_dropoff_site = True - self._set_carrier(self.poste_carrier) - self.dropoff_site_foo = self.env["dropoff.site"].create( - {"ref": "foo", "name": "Foo", "carrier_id": self.poste_carrier.id} - ) - self.dropoff_site_bar = self.env["dropoff.site"].create( - {"ref": "bar", "name": "Bar", "carrier_id": self.poste_carrier.id} - ) - self._set_dropoff_site("foo") - - def _set_dropoff_site(self, code): - self.res_cart = self.service.dispatch( - "set_dropoff_site", params={"code": code})['data'] - self.res_address = self.res_cart['shipping']['address'] - - def test_setting_dropoff_site(self): - shipping = self.cart.partner_shipping_id - self.assertEqual(shipping.ref, "foo") - self.assertEqual(shipping.name, "Foo") - self.assertEqual( - self.cart.final_shipping_partner_id, self.final_partner - ) - self.assertEqual(self.res_address['is_dropoff_site'], True) - self.assertEqual(self.res_address['recipient_name'], 'Osiris') - - def test_changing_dropoff_site(self): - previous_shipping = self.cart.partner_shipping_id - self._set_dropoff_site("bar") - self.assertNotEqual(self.cart.partner_shipping_id, previous_shipping) - shipping = self.cart.partner_shipping_id - self.assertEqual(shipping.ref, "bar") - self.assertEqual(shipping.name, "Bar") - self.assertEqual( - self.cart.final_shipping_partner_id, self.final_partner - ) - self.assertEqual(self.res_address['is_dropoff_site'], True) - self.assertEqual(self.res_address['recipient_name'], 'Osiris') - - def test_change_carrier(self): - cart = self._set_carrier(self.free_carrier) - address = cart['shipping']['address'] - self.assertEqual(self.cart.partner_shipping_id, self.final_partner) - self.assertNotIn('is_dropoff_site', address) - self.assertNotIn('recipient_name', address) - - def test_unset_carrier(self): - self.service._unset_carrier(self.cart) - self.assertEqual(self.cart.partner_shipping_id, self.final_partner) From 15e0dc0a24e4f40a7e084f4ff9454e323f3402f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Wed, 22 Apr 2020 17:14:35 +0200 Subject: [PATCH 09/18] [FIX] fix changed in API --- shopinvader_delivery_pickup/services/delivery_carrier.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shopinvader_delivery_pickup/services/delivery_carrier.py b/shopinvader_delivery_pickup/services/delivery_carrier.py index 04dc264..1e01dd4 100644 --- a/shopinvader_delivery_pickup/services/delivery_carrier.py +++ b/shopinvader_delivery_pickup/services/delivery_carrier.py @@ -15,8 +15,8 @@ def allowed_carrier_types(self): res.append("pickup") return res - def _prepare_carrier(self, carrier): - res = super(DeliveryCarrierService, self)._prepare_carrier(carrier) + def _prepare_carrier(self, carrier, no_price=False): + res = super(DeliveryCarrierService, self)._prepare_carrier(carrier, no_price=no_price) if carrier.with_dropoff_site: res["type"] = "pickup" return res From 742faa886a16b31484012fb8d060bced9695211a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Tue, 25 Aug 2020 17:45:26 +0200 Subject: [PATCH 10/18] [FIX] fix comment done by Laurent Mignon --- shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst | 1 + shopinvader_delivery_pickup/services/delivery_pickup.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst b/shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst index 4be8cbf..cfb1e7a 100644 --- a/shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst +++ b/shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst @@ -1 +1,2 @@ * Sebastien BEAU +* Laurent Mignon diff --git a/shopinvader_delivery_pickup/services/delivery_pickup.py b/shopinvader_delivery_pickup/services/delivery_pickup.py index 8b9b0ad..4bc3b7b 100644 --- a/shopinvader_delivery_pickup/services/delivery_pickup.py +++ b/shopinvader_delivery_pickup/services/delivery_pickup.py @@ -145,3 +145,10 @@ def _json_parser(self): ("country_id:country", ["id", "name"]), ("carrier_id:carrier", ["id", "name"]), ] + + +class DeliveryPickupServiceDeprecated(Component): + _inherit = "shopinvader.delivery.pickup.service" + _name = "shopinvader.deprecated.delivery.pickup.service" + _usage = "delivery_pickup" + _description = "Deprecated Service use 'delivery_pickups' instead" From c3845fb3ed908f95d6ab8ca7239c8fe66e127c54 Mon Sep 17 00:00:00 2001 From: hparfr Date: Tue, 8 Jun 2021 16:26:41 +0200 Subject: [PATCH 11/18] shopinvader_delivery_pickup: black, isort, prettier --- shopinvader_delivery_pickup/services/delivery_carrier.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shopinvader_delivery_pickup/services/delivery_carrier.py b/shopinvader_delivery_pickup/services/delivery_carrier.py index 1e01dd4..a1e6464 100644 --- a/shopinvader_delivery_pickup/services/delivery_carrier.py +++ b/shopinvader_delivery_pickup/services/delivery_carrier.py @@ -16,7 +16,9 @@ def allowed_carrier_types(self): return res def _prepare_carrier(self, carrier, no_price=False): - res = super(DeliveryCarrierService, self)._prepare_carrier(carrier, no_price=no_price) + res = super(DeliveryCarrierService, self)._prepare_carrier( + carrier, no_price=no_price + ) if carrier.with_dropoff_site: res["type"] = "pickup" return res From cef0a0cf2b0a11d2e34f9e4b3c2644676ddc6d79 Mon Sep 17 00:00:00 2001 From: hparfr Date: Tue, 8 Jun 2021 16:26:41 +0200 Subject: [PATCH 12/18] shopinvader_delivery_pickup: Migration to 12.0 --- shopinvader_delivery_pickup/__init__.py | 2 -- shopinvader_delivery_pickup/__manifest__.py | 3 +-- .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 7 +----- .../services/__init__.py | 2 -- .../services/abstract_sale.py | 1 - shopinvader_delivery_pickup/services/cart.py | 1 - .../services/delivery_carrier.py | 7 +++--- .../services/delivery_pickup.py | 23 +++++++------------ shopinvader_delivery_pickup/tests/common.py | 3 +-- .../tests/test_cart.py | 1 - .../tests/test_delivery_carrier.py | 3 +-- .../tests/test_delivery_pickup.py | 1 - 13 files changed, 16 insertions(+), 39 deletions(-) diff --git a/shopinvader_delivery_pickup/__init__.py b/shopinvader_delivery_pickup/__init__.py index 54b9afb..99464a7 100644 --- a/shopinvader_delivery_pickup/__init__.py +++ b/shopinvader_delivery_pickup/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import services diff --git a/shopinvader_delivery_pickup/__manifest__.py b/shopinvader_delivery_pickup/__manifest__.py index febb566..1ca11c7 100644 --- a/shopinvader_delivery_pickup/__manifest__.py +++ b/shopinvader_delivery_pickup/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2019 Akretion (http://www.akretion.com) # Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). @@ -6,7 +5,7 @@ { "name": "Shopinvader Delivery pickup", "summary": "Allows to deliver sale order to pickup site", - "version": "10.0.0.0.0", + "version": "12.0.1.0.0", "category": "e-commerce", "website": "https://github.com/shopinvader/odoo-shopinvader-carrier", "author": "Akretion, ACSONE SA/NV", diff --git a/shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst b/shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst index cfb1e7a..f9f4dfc 100644 --- a/shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst +++ b/shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst @@ -1,2 +1,3 @@ * Sebastien BEAU * Laurent Mignon +* Raphaël Reverdy diff --git a/shopinvader_delivery_pickup/readme/DESCRIPTION.rst b/shopinvader_delivery_pickup/readme/DESCRIPTION.rst index 72ae26e..e605b24 100644 --- a/shopinvader_delivery_pickup/readme/DESCRIPTION.rst +++ b/shopinvader_delivery_pickup/readme/DESCRIPTION.rst @@ -1,6 +1 @@ -This is shopinvader the odoo module for the new generation of e-commerce. - -This module is a base module for supporting dropoffsite - - -.. _Shopinvader: https://shopinvader.com +Expose pickup sites to shopinvader API diff --git a/shopinvader_delivery_pickup/services/__init__.py b/shopinvader_delivery_pickup/services/__init__.py index f578e20..41d27e5 100644 --- a/shopinvader_delivery_pickup/services/__init__.py +++ b/shopinvader_delivery_pickup/services/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from . import abstract_sale from . import cart from . import delivery_pickup diff --git a/shopinvader_delivery_pickup/services/abstract_sale.py b/shopinvader_delivery_pickup/services/abstract_sale.py index a50e3be..0376b58 100644 --- a/shopinvader_delivery_pickup/services/abstract_sale.py +++ b/shopinvader_delivery_pickup/services/abstract_sale.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2016 Akretion (http://www.akretion.com) # Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). diff --git a/shopinvader_delivery_pickup/services/cart.py b/shopinvader_delivery_pickup/services/cart.py index 9414323..6d615ff 100644 --- a/shopinvader_delivery_pickup/services/cart.py +++ b/shopinvader_delivery_pickup/services/cart.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2019 Akretion (http://www.akretion.com). # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/shopinvader_delivery_pickup/services/delivery_carrier.py b/shopinvader_delivery_pickup/services/delivery_carrier.py index a1e6464..71240a2 100644 --- a/shopinvader_delivery_pickup/services/delivery_carrier.py +++ b/shopinvader_delivery_pickup/services/delivery_carrier.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- # Copyright 2019 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # pylint: disable=consider-merging-classes-inherited,method-required-super -from openerp.addons.component.core import Component +from odoo.addons.component.core import Component class DeliveryCarrierService(Component): @@ -15,9 +14,9 @@ def allowed_carrier_types(self): res.append("pickup") return res - def _prepare_carrier(self, carrier, no_price=False): + def _prepare_carrier(self, carrier, cart=None): res = super(DeliveryCarrierService, self)._prepare_carrier( - carrier, no_price=no_price + carrier, cart ) if carrier.with_dropoff_site: res["type"] = "pickup" diff --git a/shopinvader_delivery_pickup/services/delivery_pickup.py b/shopinvader_delivery_pickup/services/delivery_pickup.py index 4bc3b7b..9c74bff 100644 --- a/shopinvader_delivery_pickup/services/delivery_pickup.py +++ b/shopinvader_delivery_pickup/services/delivery_pickup.py @@ -1,16 +1,15 @@ -# -*- coding: utf-8 -*- # Copyright 2019 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp.addons.base_rest.components.service import to_int -from openerp.addons.component.core import Component -from openerp.osv.expression import FALSE_DOMAIN +from odoo.addons.base_rest.components.service import to_int +from odoo.addons.component.core import Component +from odoo.osv.expression import FALSE_DOMAIN class DeliveryPickupService(Component): _inherit = "base.shopinvader.service" _name = "shopinvader.delivery.pickup.service" - _usage = "delivery_pickups" + _usage = "delivery_pickup" _description = """ This service allows you to retrieve the information of available pickup sites. @@ -20,7 +19,7 @@ class DeliveryPickupService(Component): def search(self, **params): """ - Returns the list of available pickup sitecs + Returns the list of available pickup sites If the target params == current_cart, the list will be limited to the pickup sites linked to carriers applying to the current cart. @@ -118,8 +117,8 @@ def _search(self, **params): def _search_param_to_domain(self, **params): # first of all, always restrict dropoff site for available carrier - available_carriers = self.component(usage="delivery_carriers")._search( - target=params.get("target") + available_carriers = self.component(usage="delivery_carrier")._search( + cart=params.get("target") ) carrier_id = params.get("carrier_id") if carrier_id: @@ -144,11 +143,5 @@ def _json_parser(self): ("state_id:state", ["id", "name"]), ("country_id:country", ["id", "name"]), ("carrier_id:carrier", ["id", "name"]), + ("attendance_ids:attendances", self._json_parser_attendances()), ] - - -class DeliveryPickupServiceDeprecated(Component): - _inherit = "shopinvader.delivery.pickup.service" - _name = "shopinvader.deprecated.delivery.pickup.service" - _usage = "delivery_pickup" - _description = "Deprecated Service use 'delivery_pickups' instead" diff --git a/shopinvader_delivery_pickup/tests/common.py b/shopinvader_delivery_pickup/tests/common.py index 79ff29e..c258fde 100644 --- a/shopinvader_delivery_pickup/tests/common.py +++ b/shopinvader_delivery_pickup/tests/common.py @@ -1,10 +1,9 @@ -# -*- coding: utf-8 -*- # Copyright 2019 Akretion (http://www.akretion.com). # Copyright 2019 ACSONE SA/NV # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp.addons.shopinvader_delivery_carrier.tests.common import ( +from odoo.addons.shopinvader_delivery_carrier.tests.common import ( CommonCarrierCase, ) diff --git a/shopinvader_delivery_pickup/tests/test_cart.py b/shopinvader_delivery_pickup/tests/test_cart.py index aa52903..9928474 100644 --- a/shopinvader_delivery_pickup/tests/test_cart.py +++ b/shopinvader_delivery_pickup/tests/test_cart.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2019 Akretion (http://www.akretion.com). # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/shopinvader_delivery_pickup/tests/test_delivery_carrier.py b/shopinvader_delivery_pickup/tests/test_delivery_carrier.py index 2500a27..f7ab250 100644 --- a/shopinvader_delivery_pickup/tests/test_delivery_carrier.py +++ b/shopinvader_delivery_pickup/tests/test_delivery_carrier.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- # Copyright 2019 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp.addons.shopinvader_delivery_carrier.tests.common import ( +from odoo.addons.shopinvader_delivery_carrier.tests.common import ( CommonCarrierCase, ) diff --git a/shopinvader_delivery_pickup/tests/test_delivery_pickup.py b/shopinvader_delivery_pickup/tests/test_delivery_pickup.py index 836204a..5cd1ce5 100644 --- a/shopinvader_delivery_pickup/tests/test_delivery_pickup.py +++ b/shopinvader_delivery_pickup/tests/test_delivery_pickup.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2019 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). From 2f9264a32ed942f66cb0c3a8df29a88367e2fe8f Mon Sep 17 00:00:00 2001 From: hparfr Date: Tue, 8 Jun 2021 17:03:37 +0200 Subject: [PATCH 13/18] shopinvader_delivery_pickup: expose opening hours --- .../services/delivery_pickup.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/shopinvader_delivery_pickup/services/delivery_pickup.py b/shopinvader_delivery_pickup/services/delivery_pickup.py index 9c74bff..007a369 100644 --- a/shopinvader_delivery_pickup/services/delivery_pickup.py +++ b/shopinvader_delivery_pickup/services/delivery_pickup.py @@ -102,6 +102,19 @@ def _pickup_site_schema(self): "name": {"type": "string", "required": True}, }, }, + "attendances": { + "type": "list", + "nullable": True, + "schema": { + "type": "dict", + "schema": { + "id": {"type": "integer", "nullable": True}, + "dayofweek": {"type": "string", "nullable": True}, + "hour_from": {"type": "number", "nullable": True}, + "hour_to": {"type": "number", "nullable": True}, + }, + }, + }, } # Services implementation @@ -145,3 +158,6 @@ def _json_parser(self): ("carrier_id:carrier", ["id", "name"]), ("attendance_ids:attendances", self._json_parser_attendances()), ] + + def _json_parser_attendances(self): + return ["id", "hour_from", "hour_to", "dayofweek"] From 0ca4616a80aba4088be1073f19e8268bcd61e674 Mon Sep 17 00:00:00 2001 From: "chafique.delli" Date: Tue, 30 Apr 2024 18:13:10 +0200 Subject: [PATCH 14/18] [16.0][MIG] shopinvader_delivery_pickup: Migration to 16.0 (FastAPI) --- setup/.setuptools-odoo-make-default-ignore | 2 + setup/README | 2 + .../addons/shopinvader_api_delivery_pickup | 1 + .../shopinvader_api_delivery_pickup/setup.py | 6 + shopinvader_api_delivery_pickup/README.rst | 81 ++++ shopinvader_api_delivery_pickup/__init__.py | 1 + .../__manifest__.py | 15 +- .../readme/CONTRIBUTORS.rst | 1 + .../readme/CREDITS.rst | 0 .../readme/DESCRIPTION.rst | 0 .../readme/ROADMAP.rst | 4 + .../routers/__init__.py | 2 + .../routers/cart.py | 84 ++++ .../routers/delivery_pickup.py | 68 +++ .../schemas/__init__.py | 8 + .../schemas/address.py | 14 + .../schemas/delivery_carrier.py | 14 + .../schemas/delivery_pickup.py | 78 ++++ .../schemas/resource_calendar_attendance.py | 26 ++ .../security/acl_delivery_pickup.xml | 19 + .../security/groups.xml | 16 + .../static/description/index.html | 436 ++++++++++++++++++ .../tests/__init__.py | 0 .../tests/common.py | 45 ++ .../tests/test_cart.py | 64 +++ .../tests/test_delivery_carrier.py | 42 ++ .../tests/test_delivery_pickup.py | 49 +- shopinvader_delivery_pickup/__init__.py | 1 - .../readme/ROADMAP.rst | 4 - .../services/__init__.py | 4 - .../services/abstract_sale.py | 20 - shopinvader_delivery_pickup/services/cart.py | 59 --- .../services/delivery_carrier.py | 23 - .../services/delivery_pickup.py | 163 ------- shopinvader_delivery_pickup/tests/common.py | 36 -- .../tests/test_cart.py | 43 -- .../tests/test_delivery_carrier.py | 38 -- 37 files changed, 1053 insertions(+), 416 deletions(-) create mode 100644 setup/.setuptools-odoo-make-default-ignore create mode 100644 setup/README create mode 120000 setup/shopinvader_api_delivery_pickup/odoo/addons/shopinvader_api_delivery_pickup create mode 100644 setup/shopinvader_api_delivery_pickup/setup.py create mode 100644 shopinvader_api_delivery_pickup/README.rst create mode 100644 shopinvader_api_delivery_pickup/__init__.py rename {shopinvader_delivery_pickup => shopinvader_api_delivery_pickup}/__manifest__.py (55%) rename {shopinvader_delivery_pickup => shopinvader_api_delivery_pickup}/readme/CONTRIBUTORS.rst (75%) rename {shopinvader_delivery_pickup => shopinvader_api_delivery_pickup}/readme/CREDITS.rst (100%) rename {shopinvader_delivery_pickup => shopinvader_api_delivery_pickup}/readme/DESCRIPTION.rst (100%) create mode 100644 shopinvader_api_delivery_pickup/readme/ROADMAP.rst create mode 100644 shopinvader_api_delivery_pickup/routers/__init__.py create mode 100644 shopinvader_api_delivery_pickup/routers/cart.py create mode 100644 shopinvader_api_delivery_pickup/routers/delivery_pickup.py create mode 100644 shopinvader_api_delivery_pickup/schemas/__init__.py create mode 100644 shopinvader_api_delivery_pickup/schemas/address.py create mode 100644 shopinvader_api_delivery_pickup/schemas/delivery_carrier.py create mode 100644 shopinvader_api_delivery_pickup/schemas/delivery_pickup.py create mode 100644 shopinvader_api_delivery_pickup/schemas/resource_calendar_attendance.py create mode 100644 shopinvader_api_delivery_pickup/security/acl_delivery_pickup.xml create mode 100644 shopinvader_api_delivery_pickup/security/groups.xml create mode 100644 shopinvader_api_delivery_pickup/static/description/index.html rename {shopinvader_delivery_pickup => shopinvader_api_delivery_pickup}/tests/__init__.py (100%) create mode 100644 shopinvader_api_delivery_pickup/tests/common.py create mode 100644 shopinvader_api_delivery_pickup/tests/test_cart.py create mode 100644 shopinvader_api_delivery_pickup/tests/test_delivery_carrier.py rename {shopinvader_delivery_pickup => shopinvader_api_delivery_pickup}/tests/test_delivery_pickup.py (63%) delete mode 100644 shopinvader_delivery_pickup/__init__.py delete mode 100644 shopinvader_delivery_pickup/readme/ROADMAP.rst delete mode 100644 shopinvader_delivery_pickup/services/__init__.py delete mode 100644 shopinvader_delivery_pickup/services/abstract_sale.py delete mode 100644 shopinvader_delivery_pickup/services/cart.py delete mode 100644 shopinvader_delivery_pickup/services/delivery_carrier.py delete mode 100644 shopinvader_delivery_pickup/services/delivery_pickup.py delete mode 100644 shopinvader_delivery_pickup/tests/common.py delete mode 100644 shopinvader_delivery_pickup/tests/test_cart.py delete mode 100644 shopinvader_delivery_pickup/tests/test_delivery_carrier.py diff --git a/setup/.setuptools-odoo-make-default-ignore b/setup/.setuptools-odoo-make-default-ignore new file mode 100644 index 0000000..207e615 --- /dev/null +++ b/setup/.setuptools-odoo-make-default-ignore @@ -0,0 +1,2 @@ +# addons listed in this file are ignored by +# setuptools-odoo-make-default (one addon per line) diff --git a/setup/README b/setup/README new file mode 100644 index 0000000..a63d633 --- /dev/null +++ b/setup/README @@ -0,0 +1,2 @@ +To learn more about this directory, please visit +https://pypi.python.org/pypi/setuptools-odoo diff --git a/setup/shopinvader_api_delivery_pickup/odoo/addons/shopinvader_api_delivery_pickup b/setup/shopinvader_api_delivery_pickup/odoo/addons/shopinvader_api_delivery_pickup new file mode 120000 index 0000000..26dfe6a --- /dev/null +++ b/setup/shopinvader_api_delivery_pickup/odoo/addons/shopinvader_api_delivery_pickup @@ -0,0 +1 @@ +../../../../shopinvader_api_delivery_pickup \ No newline at end of file diff --git a/setup/shopinvader_api_delivery_pickup/setup.py b/setup/shopinvader_api_delivery_pickup/setup.py new file mode 100644 index 0000000..28c57bb --- /dev/null +++ b/setup/shopinvader_api_delivery_pickup/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/shopinvader_api_delivery_pickup/README.rst b/shopinvader_api_delivery_pickup/README.rst new file mode 100644 index 0000000..f4c7ffd --- /dev/null +++ b/shopinvader_api_delivery_pickup/README.rst @@ -0,0 +1,81 @@ +=========================== +Shopinvader Delivery Pickup +=========================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:d1c0d9ae60c51081ab91a8e029129c55cea1cc48162865c0fa7ae2bee91e9c05 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-shopinvader%2Fodoo--shopinvader--carrier-lightgray.png?logo=github + :target: https://github.com/shopinvader/odoo-shopinvader-carrier/tree/16.0/shopinvader_api_delivery_pickup + :alt: shopinvader/odoo-shopinvader-carrier + +|badge1| |badge2| |badge3| + +Expose pickup sites to shopinvader API + +**Table of contents** + +.. contents:: + :local: + +Known issues / Roadmap +====================== + +For now the module only implement a fastapi for setting the dropoffsite. + +TODO: +- add the possibility to search a dropoff_site with geo location + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Akretion +* ACSONE SA/NV +* Shopinvader + +Contributors +~~~~~~~~~~~~ + +* Sebastien BEAU +* Laurent Mignon +* Raphaël Reverdy +* Chafique DELLI + +Other credits +~~~~~~~~~~~~~ + +The development of this module has been financially supported by: + +* Akretion +* ACSONE SA/NV + +Maintainers +~~~~~~~~~~~ + +This module is part of the `shopinvader/odoo-shopinvader-carrier `_ project on GitHub. + +You are welcome to contribute. diff --git a/shopinvader_api_delivery_pickup/__init__.py b/shopinvader_api_delivery_pickup/__init__.py new file mode 100644 index 0000000..62a5d54 --- /dev/null +++ b/shopinvader_api_delivery_pickup/__init__.py @@ -0,0 +1 @@ +from . import routers diff --git a/shopinvader_delivery_pickup/__manifest__.py b/shopinvader_api_delivery_pickup/__manifest__.py similarity index 55% rename from shopinvader_delivery_pickup/__manifest__.py rename to shopinvader_api_delivery_pickup/__manifest__.py index 1ca11c7..463d0d3 100644 --- a/shopinvader_delivery_pickup/__manifest__.py +++ b/shopinvader_api_delivery_pickup/__manifest__.py @@ -1,20 +1,23 @@ -# Copyright 2019 Akretion (http://www.akretion.com) +# Copyright 2019-2024 Akretion (http://www.akretion.com) # Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { - "name": "Shopinvader Delivery pickup", + "name": "Shopinvader Delivery Pickup", "summary": "Allows to deliver sale order to pickup site", - "version": "12.0.1.0.0", + "version": "16.0.1.0.0", "category": "e-commerce", "website": "https://github.com/shopinvader/odoo-shopinvader-carrier", - "author": "Akretion, ACSONE SA/NV", + "author": "Akretion, ACSONE SA/NV, Shopinvader", "license": "AGPL-3", "application": False, "installable": True, "external_dependencies": {"python": [], "bin": []}, - "depends": ["shopinvader_delivery_carrier", "delivery_dropoff_site"], - "data": [], + "depends": ["shopinvader_api_delivery_carrier", "delivery_dropoff_site"], + "data": [ + "security/groups.xml", + "security/acl_delivery_pickup.xml", + ], "demo": [], "qweb": [], } diff --git a/shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst b/shopinvader_api_delivery_pickup/readme/CONTRIBUTORS.rst similarity index 75% rename from shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst rename to shopinvader_api_delivery_pickup/readme/CONTRIBUTORS.rst index f9f4dfc..ac6ec40 100644 --- a/shopinvader_delivery_pickup/readme/CONTRIBUTORS.rst +++ b/shopinvader_api_delivery_pickup/readme/CONTRIBUTORS.rst @@ -1,3 +1,4 @@ * Sebastien BEAU * Laurent Mignon * Raphaël Reverdy +* Chafique DELLI diff --git a/shopinvader_delivery_pickup/readme/CREDITS.rst b/shopinvader_api_delivery_pickup/readme/CREDITS.rst similarity index 100% rename from shopinvader_delivery_pickup/readme/CREDITS.rst rename to shopinvader_api_delivery_pickup/readme/CREDITS.rst diff --git a/shopinvader_delivery_pickup/readme/DESCRIPTION.rst b/shopinvader_api_delivery_pickup/readme/DESCRIPTION.rst similarity index 100% rename from shopinvader_delivery_pickup/readme/DESCRIPTION.rst rename to shopinvader_api_delivery_pickup/readme/DESCRIPTION.rst diff --git a/shopinvader_api_delivery_pickup/readme/ROADMAP.rst b/shopinvader_api_delivery_pickup/readme/ROADMAP.rst new file mode 100644 index 0000000..0667b5b --- /dev/null +++ b/shopinvader_api_delivery_pickup/readme/ROADMAP.rst @@ -0,0 +1,4 @@ +For now the module only implement a fastapi for setting the dropoffsite. + +TODO: +- add the possibility to search a dropoff_site with geo location diff --git a/shopinvader_api_delivery_pickup/routers/__init__.py b/shopinvader_api_delivery_pickup/routers/__init__.py new file mode 100644 index 0000000..0896596 --- /dev/null +++ b/shopinvader_api_delivery_pickup/routers/__init__.py @@ -0,0 +1,2 @@ +from . import cart +from .delivery_pickup import delivery_pickup_router diff --git a/shopinvader_api_delivery_pickup/routers/cart.py b/shopinvader_api_delivery_pickup/routers/cart.py new file mode 100644 index 0000000..1ade256 --- /dev/null +++ b/shopinvader_api_delivery_pickup/routers/cart.py @@ -0,0 +1,84 @@ +# Copyright 2019 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from typing import Annotated + +from fastapi import Depends + +from odoo import _, api, models +from odoo.exceptions import UserError + +from odoo.addons.base.models.res_partner import Partner as ResPartner +from odoo.addons.fastapi.dependencies import ( + authenticated_partner, + authenticated_partner_env, +) +from odoo.addons.sale.models.sale_order import SaleOrder +from odoo.addons.shopinvader_api_cart.routers import cart_router +from odoo.addons.shopinvader_api_cart.schemas import CartTransaction +from odoo.addons.shopinvader_schema_sale.schemas import Sale + +from ..schemas import DeliveryPickupInput + + +@cart_router.post("/set_pickup") +@cart_router.post("/{uuid}/set_pickup") +@cart_router.post("/current/set_pickup") +def set_delivery_pickup( + env: Annotated[api.Environment, Depends(authenticated_partner_env)], + partner: Annotated["ResPartner", Depends(authenticated_partner)], + data: DeliveryPickupInput, + uuid: str | None = None, +) -> Sale | None: + """ + If cart is found, set the pickup site on it. + """ + cart = env["sale.order"]._find_open_cart(partner.id, uuid) + if not cart: + raise UserError(_("There is no cart")) + env["shopinvader_api_cart.cart_router.helper"]._set_delivery_pickup(cart, data) + return Sale.from_sale_order(cart) if cart else None + + +class ShopinvaderApiCartRouterHelper(models.AbstractModel): + _inherit = "shopinvader_api_cart.cart_router.helper" + + # Set delivery pickup + @api.model + def _set_delivery_pickup(self, cart, data): + pickup_site = self.env["dropoff.site"].search( + [("id", "=", data.pickup_site_id)] + ) + if not pickup_site: + raise UserError(_("Invalid code for pickup site")) + if pickup_site.carrier_id not in cart.shopinvader_available_carrier_ids: + raise UserError(_("This delivery method is not available for your order")) + self._set_carrier_and_price(cart, pickup_site.carrier_id.id) + vals = {"partner_shipping_id": pickup_site.partner_id.id} + if not cart.final_shipping_partner_id: + vals["final_shipping_partner_id"] = cart.partner_shipping_id.id + cart.sudo().write(vals) + + @api.model + def _reset_delivery_pickup(self, cart): + if cart.final_shipping_partner_id: + cart.partner_shipping_id = cart.final_shipping_partner_id + cart.final_shipping_partner_id = None + + @api.model + def _set_carrier(self, cart, data): + self._reset_delivery_pickup(cart) + return super()._set_carrier(cart, data) + + @api.model + def _sync_cart( + self, + partner: ResPartner, + cart: SaleOrder, + uuid: str, + transactions: list[CartTransaction], + ): + cart = super()._sync_cart(partner, cart, uuid, transactions) + if transactions: + self._reset_delivery_pickup(cart) + return cart diff --git a/shopinvader_api_delivery_pickup/routers/delivery_pickup.py b/shopinvader_api_delivery_pickup/routers/delivery_pickup.py new file mode 100644 index 0000000..9906206 --- /dev/null +++ b/shopinvader_api_delivery_pickup/routers/delivery_pickup.py @@ -0,0 +1,68 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from typing import Annotated + +from fastapi import APIRouter, Depends + +from odoo import api, models +from odoo.osv import expression + +from odoo.addons.base.models.res_partner import Partner as ResPartner +from odoo.addons.delivery_dropoff_site.models.dropoff_site import DropoffSite +from odoo.addons.fastapi.dependencies import ( + authenticated_partner, + authenticated_partner_env, +) + +from ..schemas import DeliveryPickup as DeliveryPickupSchema, DeliveryPickupSearch + +delivery_pickup_router = APIRouter(tags=["delivery_pickups"]) + + +@delivery_pickup_router.get("/delivery_pickups") +def search( + data: Annotated[DeliveryPickupSearch, Depends()], + env: Annotated[api.Environment, Depends(authenticated_partner_env)], + partner: Annotated[ResPartner, Depends(authenticated_partner)], +) -> list[DeliveryPickupSchema]: + """ + Returns the list of all available pickup sites. + + If cart, the list will be limited to the + pickup sites linked to carriers applying to the current cart. + + If you don't provide a carrier_id, the service will return all the + pickup sites linked to carriers available for this site. + + If you provide a carrier_id, only the pickup sites linked to the given + carrier are returned except if the carrier is not available for this + site. + """ + delivery_pickups = ( + env["shopinvader_api_delivery_pickup.delivery_pickup_router.helper"] + .new({"partner": partner}) + ._search(data, cart=None) + ) + return [ + DeliveryPickupSchema.from_delivery_pickup(delivery_pickup) + for delivery_pickup in delivery_pickups + ] + + +class ShopinvaderApiDeliveryRouterHelper(models.AbstractModel): + _name = "shopinvader_api_delivery_pickup.delivery_pickup_router.helper" + _description = "ShopInvader API Delivery Pickup Router Helper" + + def _search(self, data, cart=None) -> DropoffSite: + """ + Search for delivery pickup sites + :return: a list of dropoff.site + """ + cart.ensure_one() + domain = data.to_odoo_domain() + if cart: + domain = expression.AND( + domain, + [("carrier_id", "in", cart.shopinvader_available_carrier_ids.ids)], + ) + return self.env["dropoff.site"].search(domain) diff --git a/shopinvader_api_delivery_pickup/schemas/__init__.py b/shopinvader_api_delivery_pickup/schemas/__init__.py new file mode 100644 index 0000000..80f4b16 --- /dev/null +++ b/shopinvader_api_delivery_pickup/schemas/__init__.py @@ -0,0 +1,8 @@ +from .resource_calendar_attendance import ResourceCalendarAttendance +from .delivery_pickup import ( + DeliveryPickup, + DeliveryPickupInput, + DeliveryPickupSearch, +) +from .address import DeliveryAddress +from .delivery_carrier import DeliveryCarrierWithPrice diff --git a/shopinvader_api_delivery_pickup/schemas/address.py b/shopinvader_api_delivery_pickup/schemas/address.py new file mode 100644 index 0000000..a5659d4 --- /dev/null +++ b/shopinvader_api_delivery_pickup/schemas/address.py @@ -0,0 +1,14 @@ +# Copyright 2024 AKRETION +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.addons.shopinvader_schema_address import schemas + + +class DeliveryAddress(schemas.DeliveryAddress, extends=True): + is_dropoff_site: bool | None = None + + @classmethod + def from_res_partner(cls, odoo_rec): + res = super().from_res_partner(odoo_rec) + res.is_dropoff_site = odoo_rec.is_dropoff_site or None + return res diff --git a/shopinvader_api_delivery_pickup/schemas/delivery_carrier.py b/shopinvader_api_delivery_pickup/schemas/delivery_carrier.py new file mode 100644 index 0000000..81bd1a8 --- /dev/null +++ b/shopinvader_api_delivery_pickup/schemas/delivery_carrier.py @@ -0,0 +1,14 @@ +# Copyright 2024 AKRETION +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.addons.shopinvader_api_delivery_carrier.schemas import delivery_carrier + + +class DeliveryCarrierWithPrice(delivery_carrier.DeliveryCarrierWithPrice, extends=True): + with_dropoff_site: bool | None = None + + @classmethod + def from_delivery_carrier(cls, odoo_rec, cart=None): + res = super().from_delivery_carrier(odoo_rec, cart=cart) + res.with_dropoff_site = odoo_rec.with_dropoff_site or None + return res diff --git a/shopinvader_api_delivery_pickup/schemas/delivery_pickup.py b/shopinvader_api_delivery_pickup/schemas/delivery_pickup.py new file mode 100644 index 0000000..82bbb70 --- /dev/null +++ b/shopinvader_api_delivery_pickup/schemas/delivery_pickup.py @@ -0,0 +1,78 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from typing import Annotated, Field, List + +from extendable_pydantic import StrictExtendableBaseModel + +from odoo import api + +from ..schemas import ResourceCalendarAttendance + + +class DeliveryPickupInput(StrictExtendableBaseModel): + pickup_site_id: int + + +class DeliveryPickupSearch(StrictExtendableBaseModel): + name: Annotated[ + str | None, + Field( + description="When used, the search look for any delivery pickup where name " + "contains the given value case insensitively." + ), + ] = None + carrier_id: Annotated[ + int | None, + Field( + description="When used, the search look for any delivery pickup where carrier " + "contains the given value case insensitively." + ), + ] = None + + def to_odoo_domain(self, env: api.Environment): + domain = [] + if self.name: + domain.append(("name", "ilike", self.name)) + if self.carrier_id: + domain.append(("carrier_id", "ilike", self.carrier_id.id)) + return domain + + +class DeliveryPickup(StrictExtendableBaseModel): + id: int + name: str + code: str | None = None + partner_id: int + street: str | None = None + street2: str | None = None + zip: str | None = None + city: str | None = None + phone: str | None = None + state_id: int | None = None + country_id: int + carrier_id: int + calendar_id: int | None = None + attendance_ids: List[ResourceCalendarAttendance] | None = None + + @classmethod + def from_delivery_pickup(cls, odoo_rec): + return cls.model_construct( + id=odoo_rec.id, + name=odoo_rec.name, + code=odoo_rec.code or None, + partner_id=odoo_rec.partner_id.id, + street=odoo_rec.street or None, + street2=odoo_rec.street2 or None, + zip=odoo_rec.zip or None, + city=odoo_rec.city or None, + phone=odoo_rec.phone or None, + state_id=odoo_rec.state_id.id or None, + country_id=odoo_rec.country_id.id, + carrier_id=odoo_rec.carrier_id.id, + calendar_id=odoo_rec.calendar_id.id or None, + attendance_ids=[ + ResourceCalendarAttendance.from_resource_calendar_attendance(attendance) + for attendance in odoo_rec.attendance_ids + ] + or None, + ) diff --git a/shopinvader_api_delivery_pickup/schemas/resource_calendar_attendance.py b/shopinvader_api_delivery_pickup/schemas/resource_calendar_attendance.py new file mode 100644 index 0000000..5c210f5 --- /dev/null +++ b/shopinvader_api_delivery_pickup/schemas/resource_calendar_attendance.py @@ -0,0 +1,26 @@ +# Copyright 2024 AKRETION +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from extendable_pydantic import StrictExtendableBaseModel + + +class ResourceCalendarAttendance(StrictExtendableBaseModel): + id: int + name: str + hour_from: float + hour_to: float + dayofweek: str + calendar_id: int + day_period: str + + @classmethod + def from_resource_calendar_attendance(cls, odoo_rec): + return cls.model_construct( + id=odoo_rec.id, + name=odoo_rec.name, + hour_from=odoo_rec.hour_from, + hour_to=odoo_rec.hour_to, + dayofweek=odoo_rec.dayofweek, + calendar_id=odoo_rec.calendar_id.id, + day_period=odoo_rec.day_period, + ) diff --git a/shopinvader_api_delivery_pickup/security/acl_delivery_pickup.xml b/shopinvader_api_delivery_pickup/security/acl_delivery_pickup.xml new file mode 100644 index 0000000..f3742c4 --- /dev/null +++ b/shopinvader_api_delivery_pickup/security/acl_delivery_pickup.xml @@ -0,0 +1,19 @@ + + + + + + delivery.pickup shopinvader user read access + + + + + + + + + diff --git a/shopinvader_api_delivery_pickup/security/groups.xml b/shopinvader_api_delivery_pickup/security/groups.xml new file mode 100644 index 0000000..3a89c74 --- /dev/null +++ b/shopinvader_api_delivery_pickup/security/groups.xml @@ -0,0 +1,16 @@ + + + + + + Delivery Pickup User + + + + diff --git a/shopinvader_api_delivery_pickup/static/description/index.html b/shopinvader_api_delivery_pickup/static/description/index.html new file mode 100644 index 0000000..3045ce1 --- /dev/null +++ b/shopinvader_api_delivery_pickup/static/description/index.html @@ -0,0 +1,436 @@ + + + + + +Shopinvader Delivery Pickup + + + +
+

Shopinvader Delivery Pickup

+ + +

Beta License: AGPL-3 shopinvader/odoo-shopinvader-carrier

+

Expose pickup sites to shopinvader API

+

Table of contents

+ +
+

Known issues / Roadmap

+

For now the module only implement a fastapi for setting the dropoffsite.

+

TODO: +- add the possibility to search a dropoff_site with geo location

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Akretion
  • +
  • ACSONE SA/NV
  • +
  • Shopinvader
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The development of this module has been financially supported by:

+
    +
  • Akretion
  • +
  • ACSONE SA/NV
  • +
+
+
+

Maintainers

+

This module is part of the shopinvader/odoo-shopinvader-carrier project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/shopinvader_delivery_pickup/tests/__init__.py b/shopinvader_api_delivery_pickup/tests/__init__.py similarity index 100% rename from shopinvader_delivery_pickup/tests/__init__.py rename to shopinvader_api_delivery_pickup/tests/__init__.py diff --git a/shopinvader_api_delivery_pickup/tests/common.py b/shopinvader_api_delivery_pickup/tests/common.py new file mode 100644 index 0000000..8fa3b1f --- /dev/null +++ b/shopinvader_api_delivery_pickup/tests/common.py @@ -0,0 +1,45 @@ +# Copyright 2019 Akretion (http://www.akretion.com). +# Copyright 2019 ACSONE SA/NV +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import json + +from requests import Response + +from odoo.addons.shopinvader_api_cart.routers import cart_router +from odoo.addons.shopinvader_api_delivery_carrier.tests.common import ( + TestShopinvaderDeliveryCarrierCommon, +) + + +class TestShopinvaderDeliveryPickupCommon(TestShopinvaderDeliveryCarrierCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.user_with_rights.groups_id = [ + (4, cls.env.ref("sales_team.group_sale_salesman").id) + ] + cls.pickup_carrier = cls.env.ref("delivery_dropoff_site.delivery_carrier") + cls.poste_carrier.with_dropoff_site = True + cls.cart.carrier_id = cls.poste_carrier.id + cls.pickup_site_foo = cls.env["dropoff.site"].create( + {"ref": "foo", "name": "Foo", "carrier_id": cls.poste_carrier.id} + ) + cls.pickup_site_bar = cls.env["dropoff.site"].create( + {"ref": "bar", "name": "Bar", "carrier_id": cls.free_carrier.id} + ) + cls.cart.partner_shipping_id = cls.pickup_site_foo.partner_id.id + cls.cart.onchange_partner_shipping_id() + cls.final_partner = cls.cart.partner_shipping_id + + def _set_carrier(self, carrier_id): + with self._create_test_client(router=cart_router) as test_client: + data = { + "carrier_id": carrier_id, + } + response: Response = test_client.post( + "/set_carrier", content=json.dumps(data) + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(self.cart.carrier_id.id, carrier_id) + return response.json() diff --git a/shopinvader_api_delivery_pickup/tests/test_cart.py b/shopinvader_api_delivery_pickup/tests/test_cart.py new file mode 100644 index 0000000..94ed5dd --- /dev/null +++ b/shopinvader_api_delivery_pickup/tests/test_cart.py @@ -0,0 +1,64 @@ +# Copyright 2019 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import json + +from requests import Response + +from odoo.tests.common import tagged + +from odoo.addons.shopinvader_api_cart.routers import cart_router + +from .common import TestShopinvaderDeliveryPickupCommon + + +@tagged("post_install", "-at_install") +class TestCart(TestShopinvaderDeliveryPickupCommon): + def _set_pickup(self, pickup_site): + with self._create_test_client(router=cart_router) as test_client: + data = { + "pickup_site_id": pickup_site.id, + } + response: Response = test_client.post( + "/set_pickup", content=json.dumps(data) + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(self.cart.partner_shipping_id, pickup_site.partner_id) + return response.json() + + def test_setting_pickup_site(self): + shipping = self.cart.partner_shipping_id + self.assertEqual(shipping.ref, "foo") + self.assertEqual(shipping.name, "Foo") + self.assertEqual(shipping, self.final_partner) + self.assertEqual(self.final_partner.is_dropoff_site, True) + self.assertEqual( + self.cart.final_shipping_partner_id.name, "FastAPI Delivery Carrier Demo" + ) + self.assertEqual(self.cart.carrier_id, self.poste_carrier) + + def test_changing_pickup_site(self): + previous_shipping = self.cart.partner_shipping_id + self._set_pickup(self.pickup_site_bar) + self.assertNotEqual(self.cart.partner_shipping_id, previous_shipping) + shipping = self.cart.partner_shipping_id + self.assertEqual(shipping.ref, "bar") + self.assertEqual(shipping.name, "Bar") + self.assertEqual(shipping, self.final_partner) + self.assertEqual(self.final_partner.is_dropoff_site, True) + self.assertEqual( + self.cart.final_shipping_partner_id.name, "FastAPI Delivery Carrier Demo" + ) + self.assertEqual(self.cart.carrier_id, self.free_carrier) + + def test_change_carrier(self): + self._set_carrier(carrier_id=self.free_carrier.id) + self.assertEqual(self.cart.partner_shipping_id, self.final_partner) + self.assertEqual(self.final_partner.is_dropoff_site, False) + self.assertEqual( + self.cart.final_shipping_partner_id.name, "FastAPI Delivery Carrier Demo" + ) + + def test_unset_carrier(self): + self._set_carrier(carrier_id=False) + self.assertEqual(self.cart.partner_shipping_id, self.final_partner) diff --git a/shopinvader_api_delivery_pickup/tests/test_delivery_carrier.py b/shopinvader_api_delivery_pickup/tests/test_delivery_carrier.py new file mode 100644 index 0000000..e8b1c94 --- /dev/null +++ b/shopinvader_api_delivery_pickup/tests/test_delivery_carrier.py @@ -0,0 +1,42 @@ +# Copyright 2019 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from requests import Response + +from odoo.tests.common import tagged + +from odoo.addons.shopinvader_api_delivery_carrier.routers import delivery_carrier_router + +from .common import TestShopinvaderDeliveryPickupCommon + + +@tagged("post_install", "-at_install") +class TestDeliveryCarrier(TestShopinvaderDeliveryPickupCommon): + def test_search_all(self): + with self._create_test_client(router=delivery_carrier_router) as test_client: + response: Response = test_client.get("/delivery_carriers") + self.assertEqual(response.status_code, 200) + res = response.json() + expected = [ + { + "id": self.free_carrier.id, + "name": self.free_carrier.name, + "code": self.free_carrier.code or None, + }, + { + "id": self.poste_carrier.id, + "name": self.poste_carrier.name, + "code": self.poste_carrier.code or None, + }, + { + "id": self.pickup_carrier.id, + "name": self.pickup_carrier.name, + "code": self.pickup_carrier.code or None, + }, + { + "id": self.local_carrier.id, + "name": self.local_carrier.name, + "code": self.local_carrier.code or None, + }, + ] + self.assertEqual(res, expected) diff --git a/shopinvader_delivery_pickup/tests/test_delivery_pickup.py b/shopinvader_api_delivery_pickup/tests/test_delivery_pickup.py similarity index 63% rename from shopinvader_delivery_pickup/tests/test_delivery_pickup.py rename to shopinvader_api_delivery_pickup/tests/test_delivery_pickup.py index 5cd1ce5..98ec3d0 100644 --- a/shopinvader_delivery_pickup/tests/test_delivery_pickup.py +++ b/shopinvader_api_delivery_pickup/tests/test_delivery_pickup.py @@ -1,28 +1,35 @@ # Copyright 2019 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from .common import CommondDeliveryPickupCase +from odoo.tests.common import tagged -class TestDeliveryCarrier(CommondDeliveryPickupCase): +from .common import TestShopinvaderDeliveryPickupCommon + + +@tagged("post_install", "-at_install") +class TestDeliveryPickup(TestShopinvaderDeliveryPickupCommon): def _assertExpectedPickupSites(self, search_result, dropoff_sites): - self.assertIn("count", search_result) - self.assertEqual(search_result["count"], len(dropoff_sites)) - pickup_ids = [r["id"] for r in search_result["rows"]] + self.assertEqual(len(search_result), len(dropoff_sites)) + pickup_ids = [r["id"] for r in search_result] self.assertSetEqual(set(dropoff_sites.ids), set(pickup_ids)) + def _delivery_pickup_search(self, domain): + return self.env["dropoff.site"].search(domain) + def test_01(self): """ Data: - * a backend with 2 delivery methods (poste, free) + * 2 delivery methods (poste, free) * 2 dropoff_site defined for delivery la poste Test Case: * search delivery_pickup without parameters Expected result: - * 2 pickup site found + * 2 pickup sites found :return: """ - res = self._delivery_pickup_search() + domain = [("carrier_id", "in", self.cart.shopinvader_available_carrier_ids.ids)] + res = self._delivery_pickup_search(domain) self._assertExpectedPickupSites( res, self.pickup_site_foo | self.pickup_site_bar ) @@ -30,7 +37,7 @@ def test_01(self): def test_02(self): """ Data: - * a backend without delivery method + * cart without delivery method * 2 dropoff_site defined for delivery la poste Test Case: * search delivery_pickup without parameters @@ -38,14 +45,15 @@ def test_02(self): * No site found :return: """ - self.backend.write({"carrier_ids": [(5, None, None)]}) - res = self._delivery_pickup_search() + self._set_carrier(carrier_id=False) + domain = [("carrier_id", "=", False)] + res = self._delivery_pickup_search(domain) self._assertExpectedPickupSites(res, self.env["dropoff.site"].browse()) def test_03(self): """ Data: - * a backend without delivery method + * cart without delivery method * 2 dropoff_site defined for delivery la poste Test Case: * search delivery_pickup for carrier la poste @@ -53,14 +61,15 @@ def test_03(self): * No site found :return: """ - self.backend.write({"carrier_ids": [(5, None, None)]}) - res = self._delivery_pickup_search(carrier_id=self.poste_carrier.id) + self._set_carrier(carrier_id=False) + domain = [("carrier_id", "=", self.poste_carrier.id)] + res = self._delivery_pickup_search(domain) self._assertExpectedPickupSites(res, self.env["dropoff.site"].browse()) def test_04(self): """ Data: - * a backend without 2 delivery methods (poste, free) + * 2 delivery methods (poste, free) with dropoff_site * 1 dropoff_site defined for delivery la poste * 1 dropoff_site defined for delivery free Test Case: @@ -71,13 +80,14 @@ def test_04(self): """ self.pickup_site_bar.carrier_id = self.free_carrier self.pickup_site_foo.carrier_id = self.poste_carrier - res = self._delivery_pickup_search(carrier_id=self.poste_carrier.id) + domain = [("carrier_id", "=", self.poste_carrier.id)] + res = self._delivery_pickup_search(domain) self._assertExpectedPickupSites(res, self.pickup_site_foo) def test_05(self): """ Data: - * a backend without 2 delivery methods (poste, free) + * 2 delivery methods (poste, free) with dropoff_site * 1 dropoff_site defined for delivery la poste * 1 dropoff_site defined for delivery la free Test Case: @@ -89,8 +99,9 @@ def test_05(self): """ self.pickup_site_bar.carrier_id = self.free_carrier self.pickup_site_foo.carrier_id = self.poste_carrier - self._set_carrier(self.poste_carrier) - res = self._delivery_pickup_search(target="current_cart") + self._set_carrier(carrier_id=self.poste_carrier.id) + domain = [("carrier_id", "in", self.cart.shopinvader_available_carrier_ids.ids)] + res = self._delivery_pickup_search(domain) self._assertExpectedPickupSites( res, self.pickup_site_foo | self.pickup_site_bar ) diff --git a/shopinvader_delivery_pickup/__init__.py b/shopinvader_delivery_pickup/__init__.py deleted file mode 100644 index 99464a7..0000000 --- a/shopinvader_delivery_pickup/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import services diff --git a/shopinvader_delivery_pickup/readme/ROADMAP.rst b/shopinvader_delivery_pickup/readme/ROADMAP.rst deleted file mode 100644 index 518c7b7..0000000 --- a/shopinvader_delivery_pickup/readme/ROADMAP.rst +++ /dev/null @@ -1,4 +0,0 @@ -For now the module only implement an api rest for setting the dropoffsite. - -TODO: -- add the possibility to search a dropoff_site with geo location diff --git a/shopinvader_delivery_pickup/services/__init__.py b/shopinvader_delivery_pickup/services/__init__.py deleted file mode 100644 index 41d27e5..0000000 --- a/shopinvader_delivery_pickup/services/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from . import abstract_sale -from . import cart -from . import delivery_pickup -from . import delivery_carrier diff --git a/shopinvader_delivery_pickup/services/abstract_sale.py b/shopinvader_delivery_pickup/services/abstract_sale.py deleted file mode 100644 index 0376b58..0000000 --- a/shopinvader_delivery_pickup/services/abstract_sale.py +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2016 Akretion (http://www.akretion.com) -# Sébastien BEAU -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from odoo.addons.component.core import AbstractComponent - - -class AbstractSaleService(AbstractComponent): - _inherit = "shopinvader.abstract.sale.service" - - def _convert_shipping(self, sale): - res = super(AbstractSaleService, self)._convert_shipping(sale) - if sale.partner_shipping_id.is_dropoff_site: - res["address"].update( - { - "recipient_name": sale.final_shipping_partner_id.name, - "is_pickup_site": True, - } - ) - return res diff --git a/shopinvader_delivery_pickup/services/cart.py b/shopinvader_delivery_pickup/services/cart.py deleted file mode 100644 index 6d615ff..0000000 --- a/shopinvader_delivery_pickup/services/cart.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2019 Akretion (http://www.akretion.com). -# @author Sébastien BEAU -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.addons.base_rest.components.service import to_int -from odoo.addons.component.core import Component -from odoo.exceptions import UserError -from odoo.tools.translate import _ - - -class CartService(Component): - _inherit = "shopinvader.cart.service" - - # Public services - - def set_delivery_pickup(self, **params): - """ - This service will apply the given pickup site AND the linked - carrier to the current cart - :param params: Dict containing pickup site information - :return: - """ - cart = self._get() - if not cart: - raise UserError(_("There is not cart")) - else: - self._set_delivery_pickup(cart, params["pickup_site_id"]) - return self._to_json(cart) - - # Validator - - def _validator_set_delivery_pickup(self): - return {"pickup_site_id": {"coerce": to_int}} - - # Services implementation - - def _set_delivery_pickup(self, cart, pickup_site_id): - pickup_site_obj = self.env["dropoff.site"] - pickup_site = pickup_site_obj.search([("id", "=", pickup_site_id)]) - if not pickup_site: - raise UserError(_("Invalid code for pickup site")) - self._set_carrier(cart, pickup_site.carrier_id.id) - vals = {"partner_shipping_id": pickup_site.partner_id.id} - if not cart.final_shipping_partner_id: - vals["final_shipping_partner_id"] = cart.partner_shipping_id.id - cart.write(vals) - - def _reset_delivery_pickup(self, cart): - if cart.final_shipping_partner_id: - cart.partner_shipping_id = cart.final_shipping_partner_id - cart.final_shipping_partner_id = None - - def _set_carrier(self, cart, carrier_id): - self._reset_delivery_pickup(cart) - return super(CartService, self)._set_carrier(cart, carrier_id) - - def _unset_carrier(self, cart): - self._reset_delivery_pickup(cart) - return super(CartService, self)._unset_carrier(cart) diff --git a/shopinvader_delivery_pickup/services/delivery_carrier.py b/shopinvader_delivery_pickup/services/delivery_carrier.py deleted file mode 100644 index 71240a2..0000000 --- a/shopinvader_delivery_pickup/services/delivery_carrier.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2019 ACSONE SA/NV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -# pylint: disable=consider-merging-classes-inherited,method-required-super - -from odoo.addons.component.core import Component - - -class DeliveryCarrierService(Component): - _inherit = "shopinvader.delivery.carrier.service" - - @property - def allowed_carrier_types(self): - res = super(DeliveryCarrierService, self).allowed_carrier_types - res.append("pickup") - return res - - def _prepare_carrier(self, carrier, cart=None): - res = super(DeliveryCarrierService, self)._prepare_carrier( - carrier, cart - ) - if carrier.with_dropoff_site: - res["type"] = "pickup" - return res diff --git a/shopinvader_delivery_pickup/services/delivery_pickup.py b/shopinvader_delivery_pickup/services/delivery_pickup.py deleted file mode 100644 index 007a369..0000000 --- a/shopinvader_delivery_pickup/services/delivery_pickup.py +++ /dev/null @@ -1,163 +0,0 @@ -# Copyright 2019 ACSONE SA/NV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.addons.base_rest.components.service import to_int -from odoo.addons.component.core import Component -from odoo.osv.expression import FALSE_DOMAIN - - -class DeliveryPickupService(Component): - _inherit = "base.shopinvader.service" - _name = "shopinvader.delivery.pickup.service" - _usage = "delivery_pickup" - _description = """ - This service allows you to retrieve the information of available - pickup sites. - """ - - # Public services: - - def search(self, **params): - """ - Returns the list of available pickup sites - - If the target params == current_cart, the list will be limited to the - pickup sites linked to carriers applying to the current cart. - - If you don't provide a carrier_id, the service will return all the - pickup sites linked to carriers available for this site. - - If you provide a carrier_id, only the pickup sites linked to the given - carrier are returned except if the carrier is not available for this - site. - - """ - dropoff_sites = self._search(**params) - return { - "count": len(dropoff_sites), - "rows": [self._dropoff_site_to_json(ds) for ds in dropoff_sites], - } - - # Validators - - def _validator_search(self): - return { - "target": { - "type": "string", - "required": False, - "allowed": ["current_cart"], - }, - "carrier_id": { - "type": "integer", - "coerce": to_int, - "required": False, - "nullable": False, - }, - } - - def _validator_return_search(self): - return { - "count": {"type": "integer", "required": True}, - "rows": { - "type": "list", - "required": True, - "schema": { - "type": "dict", - "schema": self._pickup_site_schema(), - }, - }, - } - - def _pickup_site_schema(self): - return { - "id": {"type": "integer", "required": True}, - "name": {"type": "string", "required": True}, - "ref": {"type": "string", "nullable": True}, - "street": {"type": "string", "nullable": True}, - "street2": {"type": "string", "nullable": True}, - "zip": {"type": "string", "nullable": True}, - "city": {"type": "string", "nullable": True}, - "phone": {"type": "string", "nullable": True}, - "state": { - "type": "dict", - "nullable": True, - "schema": { - "id": {"type": "integer", "required": True}, - "name": {"type": "string", "required": True}, - }, - }, - "country": { - "type": "dict", - "nullable": True, - "schema": { - "id": {"type": "integer", "required": True}, - "name": {"type": "string", "required": True}, - }, - }, - "carrier": { - "type": "dict", - "nullable": True, - "schema": { - "id": {"type": "integer", "required": True}, - "name": {"type": "string", "required": True}, - }, - }, - "attendances": { - "type": "list", - "nullable": True, - "schema": { - "type": "dict", - "schema": { - "id": {"type": "integer", "nullable": True}, - "dayofweek": {"type": "string", "nullable": True}, - "hour_from": {"type": "number", "nullable": True}, - "hour_to": {"type": "number", "nullable": True}, - }, - }, - }, - } - - # Services implementation - - def _search(self, **params): - """ - Search for delively carriers - :param params: see _validator_search - :return: a list of delivery.carriers - """ - domain = self._search_param_to_domain(**params) - return self.env["dropoff.site"].search(domain) - - def _search_param_to_domain(self, **params): - # first of all, always restrict dropoff site for available carrier - available_carriers = self.component(usage="delivery_carrier")._search( - cart=params.get("target") - ) - carrier_id = params.get("carrier_id") - if carrier_id: - if carrier_id not in available_carriers.ids: - return FALSE_DOMAIN - return [("carrier_id", "=", carrier_id)] - return [("carrier_id", "in", available_carriers.ids)] - - def _dropoff_site_to_json(self, dropoff_site): - return dropoff_site.jsonify(self._json_parser())[0] - - def _json_parser(self): - return [ - "id", - "name", - "ref", - "street", - "street2", - "zip", - "city", - "phone", - ("state_id:state", ["id", "name"]), - ("country_id:country", ["id", "name"]), - ("carrier_id:carrier", ["id", "name"]), - ("attendance_ids:attendances", self._json_parser_attendances()), - ] - - def _json_parser_attendances(self): - return ["id", "hour_from", "hour_to", "dayofweek"] diff --git a/shopinvader_delivery_pickup/tests/common.py b/shopinvader_delivery_pickup/tests/common.py deleted file mode 100644 index c258fde..0000000 --- a/shopinvader_delivery_pickup/tests/common.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright 2019 Akretion (http://www.akretion.com). -# Copyright 2019 ACSONE SA/NV -# @author Sébastien BEAU -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo.addons.shopinvader_delivery_carrier.tests.common import ( - CommonCarrierCase, -) - - -class CommondDeliveryPickupCase(CommonCarrierCase): - def setUp(self): - super(CommondDeliveryPickupCase, self).setUp() - self.final_partner = self.cart.partner_shipping_id - self.poste_carrier.with_dropoff_site = True - self._set_carrier(self.poste_carrier) - self.pickup_site_foo = self.env["dropoff.site"].create( - {"ref": "foo", "name": "Foo", "carrier_id": self.poste_carrier.id} - ) - self.pickup_site_bar = self.env["dropoff.site"].create( - {"ref": "bar", "name": "Bar", "carrier_id": self.free_carrier.id} - ) - self.cart_service = self.service - self.delivery_pickup_service = self.cart_service.component( - usage="delivery_pickups" - ) - self._cart_set_delivery_pickup(self.pickup_site_foo.id) - - def _cart_set_delivery_pickup(self, pickup_site_id): - self.res_cart = self.cart_service.dispatch( - "set_delivery_pickup", params={"pickup_site_id": pickup_site_id} - )["data"] - self.res_address = self.res_cart["shipping"]["address"] - - def _delivery_pickup_search(self, **params): - return self.delivery_pickup_service.dispatch("search", params=params) diff --git a/shopinvader_delivery_pickup/tests/test_cart.py b/shopinvader_delivery_pickup/tests/test_cart.py deleted file mode 100644 index 9928474..0000000 --- a/shopinvader_delivery_pickup/tests/test_cart.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright 2019 Akretion (http://www.akretion.com). -# @author Sébastien BEAU -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from .common import CommondDeliveryPickupCase - - -class TestCart(CommondDeliveryPickupCase): - def test_setting_pickup_site(self): - shipping = self.cart.partner_shipping_id - self.assertEqual(shipping.ref, "foo") - self.assertEqual(shipping.name, "Foo") - self.assertEqual( - self.cart.final_shipping_partner_id, self.final_partner - ) - self.assertEqual(self.res_address["is_pickup_site"], True) - self.assertEqual(self.res_address["recipient_name"], "Osiris") - self.assertEqual(self.cart.carrier_id, self.poste_carrier) - - def test_changing_pickup_site(self): - previous_shipping = self.cart.partner_shipping_id - self._cart_set_delivery_pickup(self.pickup_site_bar.id) - self.assertNotEqual(self.cart.partner_shipping_id, previous_shipping) - shipping = self.cart.partner_shipping_id - self.assertEqual(shipping.ref, "bar") - self.assertEqual(shipping.name, "Bar") - self.assertEqual( - self.cart.final_shipping_partner_id, self.final_partner - ) - self.assertEqual(self.res_address["is_pickup_site"], True) - self.assertEqual(self.res_address["recipient_name"], "Osiris") - self.assertEqual(self.cart.carrier_id, self.free_carrier) - - def test_change_carrier(self): - cart = self._set_carrier(self.free_carrier) - address = cart["shipping"]["address"] - self.assertEqual(self.cart.partner_shipping_id, self.final_partner) - self.assertNotIn("is_pickup_site", address) - self.assertNotIn("recipient_name", address) - - def test_unset_carrier(self): - self.service._unset_carrier(self.cart) - self.assertEqual(self.cart.partner_shipping_id, self.final_partner) diff --git a/shopinvader_delivery_pickup/tests/test_delivery_carrier.py b/shopinvader_delivery_pickup/tests/test_delivery_carrier.py deleted file mode 100644 index f7ab250..0000000 --- a/shopinvader_delivery_pickup/tests/test_delivery_carrier.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2019 ACSONE SA/NV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo.addons.shopinvader_delivery_carrier.tests.common import ( - CommonCarrierCase, -) - - -class TestDeliveryCarrier(CommonCarrierCase): - def setUp(self): - super(CommonCarrierCase, self).setUp() - self.carrier_service = self.service.component("delivery_carriers") - self.poste_carrier.with_dropoff_site = True - self.pickup_site_foo = self.env["dropoff.site"].create( - {"ref": "foo", "name": "Foo", "carrier_id": self.poste_carrier.id} - ) - - def test_search_all(self): - res = self.carrier_service.search() - expected = { - "count": 2, - "rows": [ - { - "price": 0.0, - "description": None, - "id": self.free_carrier.id, - "name": self.free_carrier.name, - "type": None, - }, - { - "price": 0.0, - "description": None, - "id": self.poste_carrier.id, - "name": self.poste_carrier.name, - "type": "pickup", - }, - ], - } - self.assertDictEqual(res, expected) From 9bab98e0abe96432fe385e836f5d80cc5f31aed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Wed, 4 Sep 2024 10:46:45 +0200 Subject: [PATCH 15/18] shopinvader_api_delivery_pickup: set module in alpha --- shopinvader_api_delivery_pickup/README.rst | 9 +++++++-- shopinvader_api_delivery_pickup/__manifest__.py | 1 + .../static/description/index.html | 8 +++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/shopinvader_api_delivery_pickup/README.rst b/shopinvader_api_delivery_pickup/README.rst index f4c7ffd..2fe77fa 100644 --- a/shopinvader_api_delivery_pickup/README.rst +++ b/shopinvader_api_delivery_pickup/README.rst @@ -10,9 +10,9 @@ Shopinvader Delivery Pickup !! source digest: sha256:d1c0d9ae60c51081ab91a8e029129c55cea1cc48162865c0fa7ae2bee91e9c05 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png :target: https://odoo-community.org/page/development-status - :alt: Beta + :alt: Alpha .. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 @@ -24,6 +24,11 @@ Shopinvader Delivery Pickup Expose pickup sites to shopinvader API +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + **Table of contents** .. contents:: diff --git a/shopinvader_api_delivery_pickup/__manifest__.py b/shopinvader_api_delivery_pickup/__manifest__.py index 463d0d3..69192db 100644 --- a/shopinvader_api_delivery_pickup/__manifest__.py +++ b/shopinvader_api_delivery_pickup/__manifest__.py @@ -9,6 +9,7 @@ "category": "e-commerce", "website": "https://github.com/shopinvader/odoo-shopinvader-carrier", "author": "Akretion, ACSONE SA/NV, Shopinvader", + "development_status": "Alpha", "license": "AGPL-3", "application": False, "installable": True, diff --git a/shopinvader_api_delivery_pickup/static/description/index.html b/shopinvader_api_delivery_pickup/static/description/index.html index 3045ce1..bf30f61 100644 --- a/shopinvader_api_delivery_pickup/static/description/index.html +++ b/shopinvader_api_delivery_pickup/static/description/index.html @@ -368,8 +368,14 @@

Shopinvader Delivery Pickup

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:d1c0d9ae60c51081ab91a8e029129c55cea1cc48162865c0fa7ae2bee91e9c05 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 shopinvader/odoo-shopinvader-carrier

+

Alpha License: AGPL-3 shopinvader/odoo-shopinvader-carrier

Expose pickup sites to shopinvader API

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+

Table of contents

    From d87684c3501559fdc42cd83f1e3ba7d1ea6a31d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Wed, 4 Sep 2024 10:51:26 +0200 Subject: [PATCH 16/18] shopinvader_api_delivery_pickup: fix import --- shopinvader_api_delivery_pickup/schemas/delivery_pickup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shopinvader_api_delivery_pickup/schemas/delivery_pickup.py b/shopinvader_api_delivery_pickup/schemas/delivery_pickup.py index 82bbb70..71b62a0 100644 --- a/shopinvader_api_delivery_pickup/schemas/delivery_pickup.py +++ b/shopinvader_api_delivery_pickup/schemas/delivery_pickup.py @@ -1,8 +1,9 @@ # Copyright 2019 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from typing import Annotated, Field, List +from typing import Annotated, List from extendable_pydantic import StrictExtendableBaseModel +from pydantic import Field from odoo import api From fc80b3c2e0402d7730a74a133f4543cb0677d84b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Thu, 5 Sep 2024 09:10:41 +0200 Subject: [PATCH 17/18] shopinvader_api_delivery_pickup: Fix setting delivery pickup --- shopinvader_api_delivery_pickup/routers/cart.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/shopinvader_api_delivery_pickup/routers/cart.py b/shopinvader_api_delivery_pickup/routers/cart.py index 1ade256..353f3da 100644 --- a/shopinvader_api_delivery_pickup/routers/cart.py +++ b/shopinvader_api_delivery_pickup/routers/cart.py @@ -36,7 +36,9 @@ def set_delivery_pickup( cart = env["sale.order"]._find_open_cart(partner.id, uuid) if not cart: raise UserError(_("There is no cart")) - env["shopinvader_api_cart.cart_router.helper"]._set_delivery_pickup(cart, data) + env["shopinvader_api_cart.cart_router.helper"]._set_delivery_pickup( + cart, data.pickup_site_id + ) return Sale.from_sale_order(cart) if cart else None @@ -45,10 +47,8 @@ class ShopinvaderApiCartRouterHelper(models.AbstractModel): # Set delivery pickup @api.model - def _set_delivery_pickup(self, cart, data): - pickup_site = self.env["dropoff.site"].search( - [("id", "=", data.pickup_site_id)] - ) + def _set_delivery_pickup(self, cart, pickup_site_id): + pickup_site = self.env["dropoff.site"].search([("id", "=", pickup_site_id)]) if not pickup_site: raise UserError(_("Invalid code for pickup site")) if pickup_site.carrier_id not in cart.shopinvader_available_carrier_ids: From 6b93a80c7c5b36c3bdda8328e076cec870634302 Mon Sep 17 00:00:00 2001 From: "chafique.delli" Date: Fri, 6 Sep 2024 14:46:25 +0200 Subject: [PATCH 18/18] shopinvader_api_delivery_pickup: add with_dropoff_site in schema --- shopinvader_api_delivery_pickup/schemas/__init__.py | 5 ++++- .../schemas/delivery_carrier.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/shopinvader_api_delivery_pickup/schemas/__init__.py b/shopinvader_api_delivery_pickup/schemas/__init__.py index 80f4b16..3714183 100644 --- a/shopinvader_api_delivery_pickup/schemas/__init__.py +++ b/shopinvader_api_delivery_pickup/schemas/__init__.py @@ -5,4 +5,7 @@ DeliveryPickupSearch, ) from .address import DeliveryAddress -from .delivery_carrier import DeliveryCarrierWithPrice +from .delivery_carrier import ( + DeliveryCarrierWithPrice, + DeliveryCarrier, +) diff --git a/shopinvader_api_delivery_pickup/schemas/delivery_carrier.py b/shopinvader_api_delivery_pickup/schemas/delivery_carrier.py index 81bd1a8..dfc3e5f 100644 --- a/shopinvader_api_delivery_pickup/schemas/delivery_carrier.py +++ b/shopinvader_api_delivery_pickup/schemas/delivery_carrier.py @@ -12,3 +12,13 @@ def from_delivery_carrier(cls, odoo_rec, cart=None): res = super().from_delivery_carrier(odoo_rec, cart=cart) res.with_dropoff_site = odoo_rec.with_dropoff_site or None return res + + +class DeliveryCarrier(delivery_carrier.DeliveryCarrier, extends=True): + with_dropoff_site: bool | None = None + + @classmethod + def from_delivery_carrier(cls, odoo_rec): + res = super().from_delivery_carrier(odoo_rec) + res.with_dropoff_site = odoo_rec.with_dropoff_site or None + return res