Skip to content

Commit

Permalink
Merge pull request #475 from itpp-labs/16.0-links-and-data
Browse files Browse the repository at this point in the history
  • Loading branch information
itpp-bot authored May 11, 2024
2 parents 0c7f129 + 067207f commit 17753a3
Show file tree
Hide file tree
Showing 17 changed files with 338 additions and 51 deletions.
4 changes: 2 additions & 2 deletions sync/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"name": "Sync 🪬 Studio",
"summary": """Join the Amazing 😍 Community ⤵️""",
"category": "VooDoo ✨ Magic",
"version": "16.0.7.0.0",
"version": "16.0.11.0.0",
"application": True,
"author": "Ivan Kropotkin",
"support": "[email protected]",
Expand All @@ -26,8 +26,8 @@
"views/sync_trigger_webhook_views.xml",
"views/sync_trigger_button_views.xml",
"views/sync_task_views.xml",
"views/sync_project_views.xml",
"views/sync_link_views.xml",
"views/sync_project_views.xml",
"data/queue_job_function_data.xml",
],
"assets": {
Expand Down
9 changes: 9 additions & 0 deletions sync/doc/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
`11.0.0`
-------

- **New:** Use prime numbers for major releases ;-)
- **New:** Support data files
- **Fix:** Use Project ID for xmlid namespace
- **New:** Support dynamic properties
- **Improvement:** make links dependent on project

`7.0.0`
-------

Expand Down
2 changes: 2 additions & 0 deletions sync/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
from . import sync_project_demo
from . import sync_task
from . import sync_job
from . import sync_data
from . import ir_logging
from . import ir_actions
from . import ir_attachment
from . import ir_fields
from . import sync_link
from . import base
123 changes: 119 additions & 4 deletions sync/models/base.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright 2020 Ivan Yelizariev <https://twitter.com/yelizariev>
# Copyright 2020,2024 Ivan Yelizariev <https://twitter.com/yelizariev>
# License MIT (https://opensource.org/licenses/MIT).

from odoo import models
from odoo import _, exceptions, models


class Base(models.AbstractModel):
Expand Down Expand Up @@ -41,14 +41,21 @@ def _create_or_update_by_xmlid(self, vals, code, namespace="XXX", module="sync")
data_obj = self.env["ir.model.data"]

res_id = data_obj._xmlid_to_res_id(xmlid_full, raise_if_not_found=False)

record = None
if res_id:
# If record exists, update it
record = self.browse(res_id)

if record and record.exists():
record.write(vals)
else:
# No record found, create a new one
record = self.create(vals)
if res_id:
# exceptional case when data record exists, but record is deleted
data_obj.search(
[("module", "=", module), ("name", "=", xmlid_code)]
).unlink()

# Also create the corresponding ir.model.data record
data_obj.create(
{
Expand All @@ -61,3 +68,111 @@ def _create_or_update_by_xmlid(self, vals, code, namespace="XXX", module="sync")
)

return record

def _set_sync_property(self, property_name, property_type, property_value):
"""
Set or create a property for the current record. If the property field
does not exist, create it dynamically.
Args:
property_name (str): Name of the property field to set.
property_value (Any): The value to assign to the property.
property_type (str): Type of the property field.
"""
Property = self.env["ir.property"]
sync_project_id = self.env.context.get("sync_project_id")

if not sync_project_id:
raise exceptions.UserError(
_("The 'sync_project_id' must be provided in the context.")
)

field_name = "x_sync_%s_%s_%s" % (sync_project_id, property_name, property_type)
field = self.env["ir.model.fields"].search(
[
("name", "=", field_name),
("model", "=", self._name),
("ttype", "=", property_type),
("sync_project_id", "=", sync_project_id),
],
limit=1,
)

if not field:
# Dynamically create the field if it does not exist
field = self.env["ir.model.fields"].create(
{
"name": field_name,
"ttype": property_type,
"model_id": self.env["ir.model"]
.search([("model", "=", self._name)], limit=1)
.id,
"field_description": property_name.capitalize().replace("_", " "),
"sync_project_id": sync_project_id, # Link to the sync project
}
)

res_id = f"{self._name},{self.id}"
prop = Property.search(
[
("name", "=", property_name),
("res_id", "=", res_id),
("fields_id", "=", field.id),
],
limit=1,
)

vals = {"type": property_type, "value": property_value}
if prop:
prop.write(vals)
else:
vals.update(
{
"name": property_name,
"fields_id": field.id,
"res_id": res_id,
}
)
Property.create(vals)

def _get_sync_property(self, property_name, property_type):
"""
Get the value of a property for the current record.
Args:
property_name (str): Name of the property field to get.
"""
Property = self.env["ir.property"]
sync_project_id = self.env.context.get("sync_project_id")

if not sync_project_id:
raise exceptions.UserError(
_("The 'sync_project_id' must be provided in the context.")
)

field_name = "x_sync_%s_%s_%s" % (sync_project_id, property_name, property_type)
field = self.env["ir.model.fields"].search(
[
("name", "=", field_name),
("model", "=", self._name),
("sync_project_id", "=", sync_project_id),
],
limit=1,
)

if not field:
raise exceptions.UserError(
f"Field '{field_name}' not found for the current model '{self._name}'."
)

res_id = f"{self._name},{self.id}"
prop = Property.search(
[
("name", "=", property_name),
("res_id", "=", res_id),
("fields_id", "=", field.id),
],
limit=1,
)

return prop.get_by_record() if prop else None
12 changes: 12 additions & 0 deletions sync/models/ir_fields.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2024 Ivan Yelizariev <https://twitter.com/yelizariev>
# License MIT (https://opensource.org/licenses/MIT).
from odoo import fields, models


class IrModelFields(models.Model):
_inherit = "ir.model.fields"

sync_project_id = fields.Many2one(
"sync.project",
string="Sync Project",
)
45 changes: 45 additions & 0 deletions sync/models/sync_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copyright 2024 Ivan Yelizariev <https://twitter.com/yelizariev>
import base64
import csv
import json
from io import StringIO

import yaml

from odoo import fields, models


class SyncData(models.Model):
_name = "sync.data"
_description = "Sync Data File"

name = fields.Char("Technical name")
project_id = fields.Many2one("sync.project", ondelete="cascade")
file_name = fields.Char("File Name")
file_content = fields.Binary("File Content")

def csv(self, *args, **kwargs):
"""Parse CSV file from binary field."""
if self.file_content:
file_content = base64.b64decode(self.file_content)
file_content = file_content.decode("utf-8")
file_like_object = StringIO(file_content)
reader = csv.DictReader(file_like_object, *args, **kwargs)
return [row for row in reader]
return []

def json(self):
"""Parse JSON file from binary field."""
if self.file_content:
file_content = base64.b64decode(self.file_content)
file_content = file_content.decode("utf-8")
return json.loads(file_content)
return {}

def yaml(self):
"""Parse YAML file from binary field."""
if self.file_content:
file_content = base64.b64decode(self.file_content)
file_content = file_content.decode("utf-8")
return yaml.safe_load(file_content)
return None
11 changes: 8 additions & 3 deletions sync/models/sync_link.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2020 Ivan Yelizariev <https://twitter.com/yelizariev>
# Copyright 2020,2024 Ivan Yelizariev <https://twitter.com/yelizariev>
# License MIT (https://opensource.org/licenses/MIT).

import logging
Expand All @@ -23,6 +23,7 @@ class SyncLink(models.Model):
_description = "Resource Links"
_order = "id desc"

project_id = fields.Char("Project")
relation = fields.Char("Relation Name", required=True)
system1 = fields.Char("System 1", required=True)
# index system2 only to make search "Odoo links"
Expand All @@ -40,7 +41,7 @@ def _auto_init(self):
self._cr,
"sync_link_refs_uniq_index",
self._table,
["relation", "system1", "system2", "ref1", "ref2", "model"],
["project_id", "relation", "system1", "system2", "ref1", "ref2", "model"],
)
return res

Expand Down Expand Up @@ -81,6 +82,7 @@ def _set_link_external(
self, relation, external_refs, sync_date=None, allow_many2many=False, model=None
):
vals = self.refs2vals(external_refs)
vals["project_id"] = self.env.context.get("sync_project_id")
# Check for existing records
if allow_many2many:
existing = self._search_links_external(relation, external_refs)
Expand Down Expand Up @@ -142,7 +144,10 @@ def _search_links_external(
self, relation, external_refs, model=None, make_logs=False
):
vals = self.refs2vals(external_refs)
domain = [("relation", "=", relation)]
domain = [
("relation", "=", relation),
("project_id", "=", self.env.context.get("sync_project_id")),
]
if model:
domain.append(("model", "=", model))
for k, v in vals.items():
Expand Down
Loading

0 comments on commit 17753a3

Please sign in to comment.