[ADD] Introduce client applications
- Created models for client applications, application specifications and application types. - Created a small technical module for adding a self-incrementing sequence field to other models. See incrementing_sequence_mixin.
This commit is contained in:
parent
947b03e125
commit
cf7a59ad98
13 changed files with 261 additions and 0 deletions
|
|
@ -458,6 +458,7 @@ class CalendarEvent(models.Model):
|
|||
partner = self.env["res.partner"].search(
|
||||
[("email", "=", _extract_vcal_email(organizer))]
|
||||
)
|
||||
# TODO: prioritize partner with a user if there is one
|
||||
return partner[0] if partner else partner # partner[0] in case many matches
|
||||
else:
|
||||
return self.env["res.partner"]
|
||||
|
|
|
|||
1
customer_applications/__init__.py
Normal file
1
customer_applications/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import models
|
||||
33
customer_applications/__manifest__.py
Normal file
33
customer_applications/__manifest__.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#
|
||||
# Bemade Inc.
|
||||
#
|
||||
# Copyright (C) 2023-June Bemade Inc. (<https://www.bemade.org>).
|
||||
# Author: Marc Durepos (Contact : marc@bemade.org)
|
||||
#
|
||||
# This program is under the terms of the GNU Lesser General Public License,
|
||||
# version 3.
|
||||
#
|
||||
# For full license details, see https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
{
|
||||
"name": "Customer Applications",
|
||||
"version": "17.0.1.0.0",
|
||||
"summary": "Adds the notion of applications to partners.",
|
||||
"category": "Contacts",
|
||||
"author": "Bemade Inc.",
|
||||
"website": "http://www.bemade.org",
|
||||
"license": "LGPL-3",
|
||||
"depends": ["contacts", "incrementing_sequence_mixin"],
|
||||
"data": [],
|
||||
"assets": {},
|
||||
"installable": True,
|
||||
"auto_install": False,
|
||||
}
|
||||
3
customer_applications/models/__init__.py
Normal file
3
customer_applications/models/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from . import application
|
||||
from . import application_specification
|
||||
from . import application_type
|
||||
34
customer_applications/models/application.py
Normal file
34
customer_applications/models/application.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from odoo import models, fields, Command
|
||||
|
||||
|
||||
class Application(models.Model):
|
||||
_name = "partner.application"
|
||||
_description = "Partner Application"
|
||||
_inherit = ["mail.thread", "mail.activity.mixin"]
|
||||
|
||||
partner_id = fields.Many2one(
|
||||
comodel_name="res.partner",
|
||||
string="Location",
|
||||
required=True,
|
||||
tracking=1,
|
||||
copy=False,
|
||||
)
|
||||
application_type_id = fields.Many2one(
|
||||
comodel_name="partner.application.type",
|
||||
required=True,
|
||||
tracking=2,
|
||||
)
|
||||
specification_ids = fields.One2many(
|
||||
comodel_name="partner.application.specification",
|
||||
inverse_name="application_id",
|
||||
tracking=3,
|
||||
)
|
||||
|
||||
def copy(self, default=None):
|
||||
self.ensure_one() # This logic won't work for batches, and it doesn't need to
|
||||
default = default or {}
|
||||
if "specification_ids" not in default:
|
||||
default["specification_ids"] = [
|
||||
Command.create(line.copy_data()[0]) for line in self.specification_ids
|
||||
]
|
||||
return super().copy(default)
|
||||
20
customer_applications/models/application_specification.py
Normal file
20
customer_applications/models/application_specification.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
from odoo import models, fields
|
||||
|
||||
|
||||
class PartnerApplicationSpecification(models.Model):
|
||||
_name = "partner.application.specification"
|
||||
_description = "Partner Application Specification"
|
||||
_inherit = ["mail.thread", "mail.activity.mixin", "incrementing.sequence.mixin"]
|
||||
_sequence_group = "application_id"
|
||||
|
||||
name = fields.Char(
|
||||
tracking=2,
|
||||
required=True,
|
||||
)
|
||||
value = fields.Text(
|
||||
tracking=2,
|
||||
)
|
||||
application_id = fields.Many2one(
|
||||
comodel_name="partner.application",
|
||||
tracking=1,
|
||||
)
|
||||
14
customer_applications/models/application_type.py
Normal file
14
customer_applications/models/application_type.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
from odoo import models, fields
|
||||
|
||||
|
||||
class PartnerApplicationType(models.Model):
|
||||
_name = "partner.application.type"
|
||||
_description = "Partner Application Type"
|
||||
|
||||
_inherit = ["mail.thread", "mail.activity.mixin"]
|
||||
|
||||
name = fields.Char(
|
||||
required=True,
|
||||
tracking=1,
|
||||
)
|
||||
description = fields.Text(tracking=2)
|
||||
1
customer_applications/tests/__init__.py
Normal file
1
customer_applications/tests/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import test_application
|
||||
59
customer_applications/tests/test_application.py
Normal file
59
customer_applications/tests/test_application.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
from odoo.tests import TransactionCase
|
||||
|
||||
|
||||
class TestApplication(TransactionCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
cls.partner_1, cls.partner_2 = cls.env["res.partner"].create(
|
||||
[
|
||||
{
|
||||
"name": "Test Partner",
|
||||
},
|
||||
{
|
||||
"name": "Test Partner 2",
|
||||
},
|
||||
]
|
||||
)
|
||||
cls.application_type = cls.env["partner.application.type"].create(
|
||||
{
|
||||
"name": "application type",
|
||||
}
|
||||
)
|
||||
|
||||
def test_copy_correctly_creates_specification_lines(self):
|
||||
application = self.env["partner.application"].create(
|
||||
{
|
||||
"partner_id": self.partner_1.id,
|
||||
"application_type_id": self.application_type.id,
|
||||
}
|
||||
)
|
||||
specifications = self.env["partner.application.specification"].create(
|
||||
[
|
||||
{
|
||||
"name": "Spec 1",
|
||||
"value": "Spec 1 value",
|
||||
"application_id": application.id,
|
||||
},
|
||||
{
|
||||
"name": "Spec 2",
|
||||
"value": "Spec 2 value",
|
||||
"application_id": application.id,
|
||||
},
|
||||
]
|
||||
)
|
||||
application_copy = application.copy(
|
||||
default={
|
||||
"partner_id": self.partner_2.id,
|
||||
}
|
||||
)
|
||||
self.assertEqual(len(application_copy.specification_ids), 2)
|
||||
self.assertEqual(len(application.specification_ids), 2)
|
||||
self.assertNotEqual(
|
||||
application.specification_ids, application_copy.specification_ids
|
||||
)
|
||||
|
||||
# TODO: move this to a test in the mixin module once we figure out how
|
||||
# to dynamically create and load models
|
||||
self.assertEqual(specifications[0].sequence, 1)
|
||||
self.assertEqual(specifications[1].sequence, 2)
|
||||
1
incrementing_sequence_mixin/__init__.py
Normal file
1
incrementing_sequence_mixin/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import models
|
||||
45
incrementing_sequence_mixin/__manifest__.py
Normal file
45
incrementing_sequence_mixin/__manifest__.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#
|
||||
# Bemade Inc.
|
||||
#
|
||||
# Copyright (C) 2023-June Bemade Inc. (<https://www.bemade.org>).
|
||||
# Author: Marc Durepos (Contact : marc@bemade.org)
|
||||
#
|
||||
# This program is under the terms of the GNU Lesser General Public License,
|
||||
# version 3.
|
||||
#
|
||||
# For full license details, see https://www.gnu.org/licenses/lgpl-3.0.en.html.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
{
|
||||
"name": "Incrementing Sequence Mixin",
|
||||
"version": "17.0.1.0.0",
|
||||
"summary": "Adds an incrementing.sequence.mixin model to inherit from.",
|
||||
"description": """
|
||||
Adds an incrementing.sequence.mixin model to inherit from. This model adds a
|
||||
sequence field to the models that inherit from it, including the logic to set it to
|
||||
the highest current sequence number + 1 by default and to leave it otherwise
|
||||
changeable by the user (i.e. with a drag and drop via the handle).
|
||||
|
||||
Specify the _sequence_group attribute on the model to indicate which field to use
|
||||
to determine if records belong to the same group. For example, if one were to use
|
||||
this on sale.order.line, they could specify "order_id" as the _sequence_group such
|
||||
that newly created lines take on the highest sequence number of all lines on the
|
||||
same sales order.
|
||||
""",
|
||||
"category": "",
|
||||
"author": "Bemade Inc.",
|
||||
"website": "http://www.bemade.org",
|
||||
"license": "LGPL-3",
|
||||
"depends": [],
|
||||
"data": [],
|
||||
"assets": {},
|
||||
"installable": True,
|
||||
"auto_install": False,
|
||||
}
|
||||
1
incrementing_sequence_mixin/models/__init__.py
Normal file
1
incrementing_sequence_mixin/models/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
|||
from . import incrementing_sequence_mixin
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class IncrementingSequenceMixin(models.AbstractModel):
|
||||
_name = "incrementing.sequence.mixin"
|
||||
_description = "Incrementing Sequence Mixin"
|
||||
_order = "sequence, id asc"
|
||||
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
super().__init_subclass__(**kwargs)
|
||||
if "incrementing.sequence.mixin" in cls._inherit:
|
||||
if not hasattr(cls, "_sequence_group"):
|
||||
raise ValueError(
|
||||
f"Model {cls._name} inheriting from incrementing.sequence.mixin must define a '_sequence_group' attribute."
|
||||
)
|
||||
|
||||
sequence = fields.Integer()
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
res = super().create(vals_list)
|
||||
for rec in res:
|
||||
if rec.sequence == 0:
|
||||
group_field = rec._sequence_group
|
||||
group_field_data = getattr(rec, group_field)
|
||||
if hasattr(group_field_data, "id"):
|
||||
group_field_data = group_field_data.id
|
||||
group = self.env[rec._name].search(
|
||||
[(group_field, "=", group_field_data)]
|
||||
)
|
||||
max_seq = max(group.mapped("sequence")) if group else 0
|
||||
rec.sequence = max_seq + 1
|
||||
return res
|
||||
|
||||
def _default_sequence(self):
|
||||
group_field = self._sequence_group
|
||||
group_field_data = getattr(self, group_field)
|
||||
if hasattr(group_field_data, "id"):
|
||||
group_field_data = group_field_data.id
|
||||
group = self.env[self._name].search([(group_field, "=", group_field_data)])
|
||||
max_seq = max(group.mapped("sequence")) if group else 0
|
||||
# Don't recalculate if already set
|
||||
for rec in self.filtered(lambda r: r.sequence == 0):
|
||||
max_seq += 1
|
||||
rec.sequence = max_seq
|
||||
|
||||
def _inverse_sequence(self):
|
||||
pass
|
||||
Loading…
Reference in a new issue