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

[15.0][IMP] usability_webhooks: update multi record #219

Merged
merged 2 commits into from
Nov 21, 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
27 changes: 27 additions & 0 deletions usability_webhooks/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,33 @@ Following successful authentication, you can proceed with five API routes:
}
}

**Note**:
If you want to attach a file to a record, you can add the key "attachment_ids" at any level of the payload.

**Example Request with Attachment**:

.. code-block:: python

{
"params": {
"model": "<model name>",
"vals": {
"search_key": {
"<key_field>": "value", # can be ID or name search string
},
"payload": {
"attachment_ids": [
{
"name": "<file_name>",
"datas": "<base64_encoded_data>"
}
],
...
}
}
}
}

Bug Tracker
===========

Expand Down
176 changes: 57 additions & 119 deletions usability_webhooks/controllers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ def _call_name_search_cache(self, model, val, args):
def _call_search_cache(self, model, val):
return model.search([("id", "=", val)])

def _get_ctx_lines(self):
"""For hooks add context to do unlink lines"""
return {}

def _get_o2m_line(self, line_data_dict, line_obj):
rec_fields = []
rec_fields_append = rec_fields.append
Expand All @@ -51,34 +47,45 @@ def _get_dict_attachment(self, list_attachment, model, res_id):
for attach in list_attachment
]

def _create_file_attachment(self, obj, data_dict, line_all_fields):
def _create_file_attachment(self, objs, data_dict, line_all_fields):
Attachment = self.env["ir.attachment"]
file_attach = self._get_dict_attachment(
data_dict.get("attachment_ids", []), obj._name, obj.id
)
for line_field in line_all_fields:
# Add attachment on o2m level 1
for i, obj_line in enumerate(obj[line_field]):
line_data_dict = data_dict[line_field][i]
file_attach += self._get_dict_attachment(
line_data_dict.get("attachment_ids", []),
obj_line._name,
obj_line.id,
)
# Find sub line in line
line_fields = self._get_o2m_line(line_data_dict, obj_line)[1]
for line_sub_field in line_fields:
# Add attachment on o2m level 2
for j, obj_sub_line in enumerate(obj_line[line_sub_field]):
sub_line_data_dict = line_data_dict[line_sub_field][j]
file_attach += self._get_dict_attachment(
sub_line_data_dict.get("attachment_ids", []),
obj_sub_line._name,
obj_sub_line.id,
)

def add_attachments(obj, data_dict, file_attach):
file_attach += self._get_dict_attachment(
data_dict.get("attachment_ids", []), obj._name, obj.id
)
for line_field, line_data in data_dict.items():
if isinstance(line_data, list) and line_field in obj:
for i, obj_line in enumerate(obj[line_field]):
line_data_dict = line_data[i]
add_attachments(obj_line, line_data_dict, file_attach)

file_attach = []
for obj in objs:
add_attachments(obj, data_dict, file_attach)

if file_attach:
Attachment.create(file_attach)

def process_lines(self, rec, data_dict, auto_create):
final_line_dict = []
final_line_append = final_line_dict.append

for line_data_dict in data_dict:
line_dict, line_fields = self._get_o2m_line(line_data_dict, rec)
line_dict = self._finalize_data_to_write(rec, line_dict, auto_create)

for line_sub_field in line_fields:
if line_sub_field in line_data_dict:
sub_line_dicts = self.process_lines(
rec[line_sub_field], line_data_dict[line_sub_field], auto_create
)
line_dict.update({line_sub_field: sub_line_dicts})

final_line_append((0, 0, line_dict))

return final_line_dict

def _convert_data_to_id(self, model, vals):
data_dict = vals.get("payload", {})
auto_create = vals.get("auto_create", {})
Expand All @@ -92,48 +99,13 @@ def _convert_data_to_id(self, model, vals):
line_all_fields.append(field)
rec_dict = {k: v for k, v in data_dict.items() if k in rec_fields}
rec_dict = self._finalize_data_to_write(rec, rec_dict, auto_create)

# Prepare Line Dict (o2m)
for line_field in line_all_fields:
final_line_dict = []
final_line_append = final_line_dict.append
# Loop all o2m lines, and recreate it
for line_data_dict in data_dict[line_field]:
line_dict, line_fields = self._get_o2m_line(
line_data_dict, rec[line_field]
)
line_dict = self._finalize_data_to_write(
rec[line_field], line_dict, auto_create
)
# Prepare Sub Line Dict (o2m)
for line_sub_field in line_fields:
final_sub_line_dict = []
final_sub_line_append = final_sub_line_dict.append
# Loop all o2m sub lines, and recreate it
for line_sub_data_dict in line_data_dict[line_sub_field]:
# Add company_id if not present
if (
"company_id" not in line_data_dict
and "company_id" in data_dict
):
line_data_dict["company_id"] = data_dict["company_id"]
sub_line_dict, line_sub_fields = self._get_o2m_line(
line_sub_data_dict, rec[line_field][line_sub_field]
)
sub_line_dict = self._finalize_data_to_write(
rec[line_field][line_sub_field], sub_line_dict, auto_create
)
final_sub_line_append((0, 0, sub_line_dict))
if line_sub_fields:
rec.clear_caches()
raise ValidationError(
_(
"friendly_create_data() support "
"2 level of one2many lines"
)
)
line_dict.update({line_sub_field: final_sub_line_dict})
final_line_append((0, 0, line_dict))
rec_dict[line_field] = final_line_dict
rec_dict[line_field] = self.process_lines(
rec[line_field], data_dict[line_field], auto_create
)

return rec_dict, rec, line_all_fields

@api.model
Expand Down Expand Up @@ -237,21 +209,12 @@ def friendly_update_data(self, model, vals):
"""
data_dict = vals.get("payload", {})
auto_create = vals.get("auto_create", {})
search_key = vals.get("search_key", {})
vals.get("search_key", {})

rec = self._search_object(model, vals)

if len(rec) > 1:
raise ValidationError(
_(
"Search key '%(key_field)s' in model '%(model)s' found mutiple matches!"
)
% {"key_field": search_key, "model": model}
)

rec_fields = []
line_all_fields = []
ctx_line = self._get_ctx_lines()

for field, model_field in rec._fields.items():
if field in data_dict and model_field.type != "one2many":
Expand All @@ -260,51 +223,23 @@ def friendly_update_data(self, model, vals):
line_all_fields.append(field)
rec_dict = {k: v for k, v in data_dict.items() if k in rec_fields}
rec_dict = self._finalize_data_to_write(rec, rec_dict, auto_create)

# Prepare Line Dict (o2m)
for line_field in line_all_fields:
lines = rec[line_field]
# First, delete all lines o2m
lines.with_context(**ctx_line).unlink()
final_line_dict = []
final_line_append = final_line_dict.append
# Loop all o2m lines, and recreate it
for line_data_dict in data_dict[line_field]:
line_dict, line_fields = self._get_o2m_line(
line_data_dict, rec[line_field]
)
line_dict = self._finalize_data_to_write(
rec[line_field], line_dict, auto_create
)
# Prepare Sub Line Dict (o2m)
for line_sub_field in line_fields:
final_sub_line_dict = []
final_sub_line_append = final_sub_line_dict.append
# Loop all o2m sub lines, and recreate it
for line_sub_data_dict in line_data_dict[line_sub_field]:
sub_line_dict, line_sub_fields = self._get_o2m_line(
line_sub_data_dict, rec[line_field][line_sub_field]
)
sub_line_dict = self._finalize_data_to_write(
rec[line_field][line_sub_field], sub_line_dict, auto_create
)
final_sub_line_append((0, 0, sub_line_dict))
if line_sub_fields:
rec.clear_caches()
raise ValidationError(
_(
"friendly_update_data() support "
"2 level of one2many lines"
)
)
line_dict.update({line_sub_field: final_sub_line_dict})
final_line_append((0, 0, line_dict))
rec_dict[line_field] = final_line_dict
lines.unlink()
rec_dict[line_field] = self.process_lines(
rec[line_field], data_dict[line_field], auto_create
)

rec.write(rec_dict)

# Create Attachment (if any)
self._create_file_attachment(rec, data_dict, line_all_fields)
res = {
"is_success": True,
"result": {"id": rec.id},
"result": {"id": rec.ids},
"messages": _("Record updated successfully"),
}
return res
Expand Down Expand Up @@ -440,7 +375,8 @@ def _finalize_data_to_write(self, rec, rec_dict, auto_create=False):
have_company = hasattr(Model, "company_id")
for val in search_vals:
# Support multi company
# orm cache can't use in type list, so we need to convert to string
# orm cache can't use in type list,
# so we need to convert to string
args = "[]"
if have_company and model not in ignore_checkcompany_model:
args = str(
Expand Down Expand Up @@ -549,11 +485,12 @@ def search_data(self, model, vals):
- Use an empty list `[]` to retrieve all fields from the model.
- Specify a list of field names `["<field_name1>", "<field_name2>"]`
to retrieve only those fields.
- For many2one, one2many and many2many fields, you can specify the fields to fetch
by using the following format:
- For many2one, one2many and many2many fields,
you can specify the fields to fetch by using the following format:
`["<field_name1>", "<field_name2>{<field_name3>, <field_name4>}"]`
where `<field_name1>` and `<field_name2>` are fields from the model,
and `<field_name3>` and `<field_name4>` are fields from the related model.
and `<field_name3>` and `<field_name4>` are fields
from the related model.
The related fields will be fetched and displayed in the result.

- search_domain:
Expand Down Expand Up @@ -612,7 +549,8 @@ def call_function(self, model, vals):
"""
Call a function on a model object based on the provided input.
Parameters (search_key) are used to search for the record:
- search_key: A dictionary containing the search criteria to find the record.
- search_key:
A dictionary containing the search criteria to find the record.

Parameters (payload) are used to call the function:
- method (str): The name of the function to call.
Expand Down
27 changes: 27 additions & 0 deletions usability_webhooks/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,30 @@ Following successful authentication, you can proceed with five API routes:
}
}
}

**Note**:
If you want to attach a file to a record, you can add the key "attachment_ids" at any level of the payload.

**Example Request with Attachment**:

.. code-block:: python

{
"params": {
"model": "<model name>",
"vals": {
"search_key": {
"<key_field>": "value", # can be ID or name search string
},
"payload": {
"attachment_ids": [
{
"name": "<file_name>",
"datas": "<base64_encoded_data>"
}
],
...
}
}
}
}
26 changes: 26 additions & 0 deletions usability_webhooks/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,32 @@ <h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
</pre>
</li>
</ol>
<p><strong>Note</strong>:
If you want to attach a file to a record, you can add the key “attachment_ids” at any level of the payload.</p>
<blockquote>
<p><strong>Example Request with Attachment</strong>:</p>
<pre class="code python literal-block">
<span class="p">{</span><span class="w">
</span> <span class="s2">&quot;params&quot;</span><span class="p">:</span> <span class="p">{</span><span class="w">
</span> <span class="s2">&quot;model&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;model name&gt;&quot;</span><span class="p">,</span><span class="w">
</span> <span class="s2">&quot;vals&quot;</span><span class="p">:</span> <span class="p">{</span><span class="w">
</span> <span class="s2">&quot;search_key&quot;</span><span class="p">:</span> <span class="p">{</span><span class="w">
</span> <span class="s2">&quot;&lt;key_field&gt;&quot;</span><span class="p">:</span> <span class="s2">&quot;value&quot;</span><span class="p">,</span> <span class="c1"># can be ID or name search string</span><span class="w">
</span> <span class="p">},</span><span class="w">
</span> <span class="s2">&quot;payload&quot;</span><span class="p">:</span> <span class="p">{</span><span class="w">
</span> <span class="s2">&quot;attachment_ids&quot;</span><span class="p">:</span> <span class="p">[</span><span class="w">
</span> <span class="p">{</span><span class="w">
</span> <span class="s2">&quot;name&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;file_name&gt;&quot;</span><span class="p">,</span><span class="w">
</span> <span class="s2">&quot;datas&quot;</span><span class="p">:</span> <span class="s2">&quot;&lt;base64_encoded_data&gt;&quot;</span><span class="w">
</span> <span class="p">}</span><span class="w">
</span> <span class="p">],</span><span class="w">
</span> <span class="o">...</span><span class="w">
</span> <span class="p">}</span><span class="w">
</span> <span class="p">}</span><span class="w">
</span> <span class="p">}</span><span class="w">
</span><span class="p">}</span>
</pre>
</blockquote>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
Expand Down
Loading