diff --git a/bemade_fsm/views/task_views.xml b/bemade_fsm/views/task_views.xml
index 6bce7cd..bd0d0e7 100644
--- a/bemade_fsm/views/task_views.xml
+++ b/bemade_fsm/views/task_views.xml
@@ -80,7 +80,7 @@
project_list
-
+
@@ -92,6 +92,12 @@
hide
+
+ hide
+
+
+ hide
+
diff --git a/caldav_sync/models/calendar_event.py b/caldav_sync/models/calendar_event.py
index 620abb0..a3a5ae8 100644
--- a/caldav_sync/models/calendar_event.py
+++ b/caldav_sync/models/calendar_event.py
@@ -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"]
diff --git a/customer_applications/__init__.py b/customer_applications/__init__.py
new file mode 100644
index 0000000..0650744
--- /dev/null
+++ b/customer_applications/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/customer_applications/__manifest__.py b/customer_applications/__manifest__.py
new file mode 100644
index 0000000..f2971fb
--- /dev/null
+++ b/customer_applications/__manifest__.py
@@ -0,0 +1,40 @@
+#
+# Bemade Inc.
+#
+# Copyright (C) 2023-June Bemade Inc. ().
+# 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": [
+ "security/groups.xml",
+ "security/ir.model.access.csv",
+ "data/menus_actions.xml",
+ "views/application_type_views.xml",
+ "views/res_partner_views.xml",
+ "views/application_views.xml",
+ ],
+ "assets": {},
+ "installable": True,
+ "auto_install": False,
+}
diff --git a/customer_applications/data/menus_actions.xml b/customer_applications/data/menus_actions.xml
new file mode 100644
index 0000000..c63bbd6
--- /dev/null
+++ b/customer_applications/data/menus_actions.xml
@@ -0,0 +1,43 @@
+
+
+
+ Application Types
+ partner.application.type
+ tree,form
+
+
-
+
fsm.equipment.treefsm.equipment
-
-
-
+
+
+
-
+ name="tag_ids"
+ widget="many2many_tags"
+ options="{'no_open': False}"
+ />
+
@@ -80,21 +100,21 @@
fsm.equipment
-
-
-
+
+
+
-
+ name="tag_ids"
+ widget="many2many_tags"
+ options="{'no_open': False}"
+ />
+
+ name="action_view_equipment"
+ type="object"
+ string="Details"
+ icon="fa-external-link"
+ />
@@ -103,34 +123,69 @@
Equipmentfsm.equipmenttree,form
+
+ {'search_default_parent_only': True}
+
+
+ fsm.equipment.search
+ fsm.equipment
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Equipment Tagfsm.equipment.tagtree,form
+
+ id="menu_contact_equipment"
+ name="Customer Equipment"
+ action="action_window_equipment"
+ sequence="3"
+ parent="contacts.menu_contacts"/>
+ id="menu_service_client"
+ name="Clients"
+ sequence="10"
+ parent="industry_fsm.fsm_menu_root"
+ groups="industry_fsm.group_fsm_user"
+ />
+ id="menu_service_client_clients"
+ name="Clients"
+ action="base.action_partner_customer_form"
+ sequence="20"
+ parent="menu_service_client"
+ groups="industry_fsm.group_fsm_user"
+ />
+
diff --git a/fsm_equipment/views/project_task_views.xml b/fsm_equipment/views/project_task_views.xml
new file mode 100644
index 0000000..d4655ac
--- /dev/null
+++ b/fsm_equipment/views/project_task_views.xml
@@ -0,0 +1,14 @@
+
+
+
+ project.task.view.tree.equipment
+ project.task
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/incrementing_sequence_mixin/__init__.py b/incrementing_sequence_mixin/__init__.py
new file mode 100644
index 0000000..0650744
--- /dev/null
+++ b/incrementing_sequence_mixin/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/incrementing_sequence_mixin/__manifest__.py b/incrementing_sequence_mixin/__manifest__.py
new file mode 100644
index 0000000..cd62f03
--- /dev/null
+++ b/incrementing_sequence_mixin/__manifest__.py
@@ -0,0 +1,45 @@
+#
+# Bemade Inc.
+#
+# Copyright (C) 2023-June Bemade Inc. ().
+# 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,
+}
diff --git a/incrementing_sequence_mixin/models/__init__.py b/incrementing_sequence_mixin/models/__init__.py
new file mode 100644
index 0000000..9dcaa0e
--- /dev/null
+++ b/incrementing_sequence_mixin/models/__init__.py
@@ -0,0 +1 @@
+from . import incrementing_sequence_mixin
diff --git a/incrementing_sequence_mixin/models/incrementing_sequence_mixin.py b/incrementing_sequence_mixin/models/incrementing_sequence_mixin.py
new file mode 100644
index 0000000..3d87d09
--- /dev/null
+++ b/incrementing_sequence_mixin/models/incrementing_sequence_mixin.py
@@ -0,0 +1,52 @@
+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)]
+ )
+ else:
+ group = None
+ 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)])
+ else:
+ group = None
+ 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
diff --git a/partner_equipment_applications/__init__.py b/partner_equipment_applications/__init__.py
new file mode 100644
index 0000000..0650744
--- /dev/null
+++ b/partner_equipment_applications/__init__.py
@@ -0,0 +1 @@
+from . import models
diff --git a/partner_equipment_applications/__manifest__.py b/partner_equipment_applications/__manifest__.py
new file mode 100644
index 0000000..4e86e60
--- /dev/null
+++ b/partner_equipment_applications/__manifest__.py
@@ -0,0 +1,36 @@
+#
+# Bemade Inc.
+#
+# Copyright (C) 2023-June Bemade Inc. ().
+# 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": "Partner Application Equipment",
+ "version": "17.0.1.0.0",
+ "summary": "Make the link between customer_applications and fsm_equipment.",
+ "category": "Services/Field Service",
+ "author": "Bemade Inc.",
+ "website": "http://www.bemade.org",
+ "license": "LGPL-3",
+ "depends": ["fsm_equipment", "customer_applications"],
+ "data": [
+ "views/equipment_views.xml",
+ "views/application_views.xml",
+ ],
+ "assets": {},
+ "installable": True,
+ "auto_install": True,
+}
diff --git a/partner_equipment_applications/models/__init__.py b/partner_equipment_applications/models/__init__.py
new file mode 100644
index 0000000..adab03c
--- /dev/null
+++ b/partner_equipment_applications/models/__init__.py
@@ -0,0 +1,2 @@
+from . import application
+from . import equipment
diff --git a/partner_equipment_applications/models/application.py b/partner_equipment_applications/models/application.py
new file mode 100644
index 0000000..7ec1352
--- /dev/null
+++ b/partner_equipment_applications/models/application.py
@@ -0,0 +1,29 @@
+from odoo import models, fields, api, _
+from odoo.exceptions import ValidationError
+
+
+class PartnerApplication(models.Model):
+ _inherit = "partner.application"
+
+ equipment_ids = fields.One2many(
+ comodel_name="fsm.equipment",
+ inverse_name="application_id",
+ string="Equipment",
+ )
+ equipment_count = fields.Integer(
+ compute="_compute_equipment_count",
+ string="Equipment Count",
+ )
+
+ @api.constrains("equipment_ids")
+ def _check_equipment_ids(self):
+ for application in self:
+ if len(application.equipment_ids.partner_id) > 1:
+ raise ValidationError(
+ _("An application can only be linked to one partner.")
+ )
+
+ @api.depends("equipment_ids")
+ def _compute_equipment_count(self):
+ for application in self:
+ application.equipment_count = len(application.equipment_ids)
diff --git a/partner_equipment_applications/models/equipment.py b/partner_equipment_applications/models/equipment.py
new file mode 100644
index 0000000..4fb8576
--- /dev/null
+++ b/partner_equipment_applications/models/equipment.py
@@ -0,0 +1,17 @@
+from odoo import models, fields
+
+
+class Equipment(models.Model):
+ _inherit = "fsm.equipment"
+
+ application_id = fields.Many2one(
+ comodel_name="partner.application",
+ tracking=1,
+ domain="[('partner_id', '=', partner_id)]",
+ ondelete="restrict",
+ )
+ application_type_id = fields.Many2one(
+ comodel_name="partner.application.type",
+ related="application_id.application_type_id",
+ readonly=True,
+ )
diff --git a/partner_equipment_applications/views/application_views.xml b/partner_equipment_applications/views/application_views.xml
new file mode 100644
index 0000000..2324949
--- /dev/null
+++ b/partner_equipment_applications/views/application_views.xml
@@ -0,0 +1,39 @@
+
+
+
+ Partner Equipments
+ fsm.equipment
+ tree,form
+ {'default_application_id': context.get("application_id")}
+ [('application_id', '=', context.get("application_id"))]
+
+
+ partner.application.search
+ partner.application
+
+
+
+
+
+
+
+
+ partner.application.form
+ partner.application
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/partner_equipment_applications/views/equipment_views.xml b/partner_equipment_applications/views/equipment_views.xml
new file mode 100644
index 0000000..2ecd9ce
--- /dev/null
+++ b/partner_equipment_applications/views/equipment_views.xml
@@ -0,0 +1,42 @@
+
+
+
+ fsm.equipment.view.search
+ fsm.equipment
+
+
+
+
+
+
+
+
+
+
+
+
+ fsm.equipment.view.tree
+ fsm.equipment
+
+
+
+
+
+
+
+
+
+
+ fsm.equipment.view.form
+ fsm.equipment
+
+
+
+
+
+
+
+
\ No newline at end of file