Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

16.0 links and data #475

Merged
merged 5 commits into from
May 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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": "info@odoomagic.com",
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
Loading