diff --git a/sync/__manifest__.py b/sync/__manifest__.py index bd105d80..9d2a9410 100644 --- a/sync/__manifest__.py +++ b/sync/__manifest__.py @@ -7,7 +7,7 @@ "name": "Sync 🪬 Studio", "summary": """Join the Amazing 😍 Community ⤵️""", "category": "VooDoo ✨ Magic", - "version": "17.0.13.0.1", + "version": "17.0.14.0.0", "application": True, "author": "Ivan Yelizariev", "support": "info@odoomagic.com", diff --git a/sync/data/sync_project_context_demo.xml b/sync/data/sync_project_context_demo.xml deleted file mode 100644 index 0daf372e..00000000 --- a/sync/data/sync_project_context_demo.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - odoo2odoo_demo - Odoo2odoo (Demo) - - - telegram_demo - Telegram (Demo) - - - trello - Trello (Demo) - - - github - Github (Demo) - - - math - Math functions (Demo) - - - diff --git a/sync/data/sync_project_odoo2odoo_demo.xml b/sync/data/sync_project_odoo2odoo_demo.xml deleted file mode 100644 index e3c1f72f..00000000 --- a/sync/data/sync_project_odoo2odoo_demo.xml +++ /dev/null @@ -1,153 +0,0 @@ - - - - - - Demo Odoo2odoo Integration - - - - - - - UPLOAD_ALL_PARTNER_PREFIX - Sync Studio: - - - - URL - - URL to external Odoo, e.g. https://odoo.example - - - - - DB - Odoo database name - - - - USERNAME - e.g. admin - - - - PASSWORD - - - - - Sync Local Partners To Remote Odoo - - - - - - - Sync Remote Partners Updates - - -", links.sync_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT)]], - fields=["write_date", IMAGE_FIELD] - ) - # Save fetched data in local Odoo - for ep in external_partners: - link = get_link(PARTNER_REL, ep["id"]) - p = link.odoo - sync_date = parse_date(ep["write_date"]) - if sync_date > link.sync_date: - p.write({IMAGE_FIELD: ep[IMAGE_FIELD]}) - link.update_links(sync_date) -]]> - - - - LOCAL_PARTNER_CREATED - - - on_create - - - CHECK_EXTERNAL_ODOO - - 15 - minutes - - - PUSH_ALL_LOCAL_PARTNERS - - - diff --git a/sync/data/sync_project_telegram_demo.xml b/sync/data/sync_project_telegram_demo.xml deleted file mode 100644 index 135aa52e..00000000 --- a/sync/data/sync_project_telegram_demo.xml +++ /dev/null @@ -1,148 +0,0 @@ - - - - - Demo Telegram Integration - - - - - - - TELEGRAM_WELCOME_MESSAGE - Hello! How can I help you? - - Message that is sent to a user on first bot usage - - - - - TELEGRAM_MESSAGE_SENT - The message has sent in telegram - - When Odoo Bot reports on successfully sent telegram message - - - - - PARTNER_NAME_PREFIX - Telegram: - Prefix for new partner name - - - - TELEGRAM_BOT_TOKEN - - - - - - Setup - - - - - - - - SETUP_TELEGRAM - - - Process Telegram Messages - - - - - - - Telegram updates - - TELEGRAM - - - Send response via Odoo - - - - - - - ON_MESSAGE_POSTED - - - on_create - - [["model","=","res.partner"],["body","ilike","/telegram"]] - - - diff --git a/sync/data/sync_project_trello_github_demo.xml b/sync/data/sync_project_trello_github_demo.xml deleted file mode 100644 index 8e592a27..00000000 --- a/sync/data/sync_project_trello_github_demo.xml +++ /dev/null @@ -1,602 +0,0 @@ - - - - - Demo Trello-Github Integration - - - - - GITHUB_TOKEN - - Token with access to read issues and create webhooks, i.e. "repo" scope - - - - - - GITHUB_REPO - owner/repo_name - - - - - TRELLO_TOKEN - - Trello token to make API calls. Don't confuse it with API Key. Once you get - API Key in https://trello.com/app-key page, you will see a link to generate - token. Click one and you'll get the token - - - - - - TRELLO_KEY - - - - - - TRELLO_BOARD_ID - - You can get one from the board url: - https://trello.com/b/TRELLO_BOARD_ID/BOARD-NAME - - - - - - ISSUE_FROM_GITHUB_PREFIX - GITHUB: - - - - - - MESSAGE_PREFIX - A Message posted on GitHub: - - - - - - LABELS_MERGE_STRATEGY - UNION - - Possible values: -USE_TRELLO, USE_GITHUB: use version from one side and override values from another -UNION: add missed labels to each side -INTERSECTION: remove labes that are not attached on another side - - - - - - Setup - - - - - - SETUP_GITHUB - Setup Github Webhook. To delete webhooks, open github repostory, navigate to Settings >> Webhooks - - - - SETUP_TRELLO - Setup Trello Webhook. - - - - DELETE_TRELLO_WEBHOOKS - - - - DEBUG - - - - Sync Github Issues to Trello (name, messages, labels) - - - - - - PUSH_ALL_ISSUES - - Initial syncing of github issues. It pushes only issues without linked - trello cards. It doesn't sync further updates - - - - - GITHUB_ISSUE_UPDATES - - Github Issue updates - - - - GITHUB_ISSUE_COMMENT - - - - - Sync Trello Cards to Github (labels) - - - - - - TRELLO_CARD_UPDATES - - Trello Card updates - - - - Sync labels Updating/Deleting - - - - - - TRELLO_LABEL_UPDATES - - Trello Label updates - - - - GITHUB_LABEL_UPDATES - - Github Label updates - - - - Labels Conflict Resolving - - - issue - issues_index = {} - for issue in issues: - if str(issue["id"]) in issue_ids: - issues_index[int(issue["id"])] = issue - log("GITHUB issues: %s" % ([issue_ids, issues, issues_index])) - - card_ids = elinks.get(TRELLO) - # https://developer.atlassian.com/cloud/trello/rest/api-group-boards/#api-boards-id-cards-get - # https://developer.atlassian.com/cloud/trello/rest/api-group-cards/#api-cards-id-get - # card is dict {id: int, idLabels: [int], ...} - cards = trello.get_all_cards() - # card_id -> card - cards_index = {} - for card in cards: - if str(card["id"]) in card_ids: - cards_index[card["id"]] = card - - for el in elinks: - card_id = el.get(TRELLO)[0] - issue_id = int(el.get(GITHUB)[0]) - card = cards_index.get(card_id) - issue = issues_index.get(issue_id) - if not (card and issue): - log("Linked card or issue is missed: %s" % ([card, issue]), level=LOG_WARNING) - continue - # compare labels - tlabel_ids = card["idLabels"] - glabel_ids = [lbl["id"] for lbl in issue["labels"]] - tlinks = search_links(LABELS_REL, {GITHUB: None, TRELLO: tlabel_ids}) - glinks = search_links(LABELS_REL, {GITHUB: glabel_ids, TRELLO: None}) - if tlinks == glinks: - # all fine - log("card {} and issue {} are already synced".format(card_id, issue_id), LOG_DEBUG) - continue - log("Found labels mismatch: issue=%s, card=%s" % (issue["id"], card["id"]), LOG_DEBUG) - - tlinks_add = None - tlinks_remove = None - glinks_add = None - glinks_remove = None - if params.LABELS_MERGE_STRATEGY == "USE_TRELLO": - glinks_add = tlinks - glinks - glinks_remove = glinks - tlinks - elif params.LABELS_MERGE_STRATEGY == "USE_GITHUB": - tlinks_add = glinks - tlinks - tlinks_remove = tlinks - glinks - elif params.LABELS_MERGE_STRATEGY == "UNION": - tlinks_add = glinks - tlinks - glinks_add = tlinks - glinks - elif params.LABELS_MERGE_STRATEGY == "INTERSECTION": - tlinks_remove = tlinks - glinks - glinks_remove = glinks - tlinks - else: - raise Exception("Unknown LABELS_MERGE_STRATEGY: %s" % LABELS_MERGE_STRATEGY, level=LOG_ERROR) - - if tlinks_add: - trello.card_add_labels(card_id, tlinks_add.get(TRELLO)) - if glinks_add: - github.issue_add_labels(issue_id, glinks_add.get(GITHUB)) - if tlinks_remove: - trello.card_remove_labels(card_id, tlinks_remove.get(TRELLO)) - if glinks_remove: - github.issue_remove_labels(issue_id, glinks_remove.get(GITHUB)) - - ]]> - - - CONFLICT_RESOLVING - - - 1 - days - - diff --git a/sync/data/sync_project_unittest_demo.xml b/sync/data/sync_project_unittest_demo.xml index 3d543c88..8644cdc4 100644 --- a/sync/data/sync_project_unittest_demo.xml +++ b/sync/data/sync_project_unittest_demo.xml @@ -15,6 +15,7 @@ Assign ref to new partners + test -from odoo import api, fields, models +# Copyright 2024-2025 Ivan Yelizariev +from odoo import fields, models class SyncOrder(models.Model): @@ -16,12 +16,17 @@ class SyncOrder(models.Model): ondelete="cascade", required=True, ) + sync_job_id = fields.Many2one("sync.job") description = fields.Html(related="sync_task_id.sync_order_description") + # DEPRECATED. Use line_ids.record_id instead record_id = fields.Reference( string="Blackjack", selection="_selection_record_id", help="Optional extra information to perform this task", ) + line_ids = fields.One2many( + "sync.order.line", "sync_order_id", string="Linked Records" + ) partner_ids = fields.Many2many("res.partner", string="Partners") state = fields.Selection( @@ -34,7 +39,6 @@ class SyncOrder(models.Model): default="draft", ) - @api.model def _selection_record_id(self): mm = self.sync_task_id.sync_order_model_id if not mm: @@ -53,3 +57,53 @@ def action_cancel(self): def action_refresh(self): # Magic pass + + +class SyncOrderLine(models.Model): + _name = "sync.order.line" + _description = "Sync Order Records" + + sync_order_id = fields.Many2one("sync.order") + record_id = fields.Reference( + string="Linked Record", + selection="_selection_record_id", + help="Optional extra information to perform this task", + ) + state = fields.Selection( + [ + ("draft", "Draft"), + ("open", "In Progress"), + ("done", "Done"), + ("error", "Failed"), + ("cancel", "Canceled"), + ], + default="draft", + ) + value = fields.Char("Extra Input") + result = fields.Char("Result") + + def _selection_record_id(self): + mm = self.sync_order_id.sync_task_id.sync_order_model_id + if not mm: + return [] + return [(mm.model, mm.name)] + + def action_done(self, msg=None): + self.write({"state": "done"}) + if msg: + self.write({"result": msg}) + + def action_confirm(self, msg=None): + self.write({"state": "open"}) + if msg: + self.write({"result": msg}) + + def action_error(self, msg=None): + self.write({"state": "error"}) + if msg: + self.write({"result": msg}) + + def action_cancel(self, msg=None): + self.write({"state": "cancel"}) + if msg: + self.write({"result": msg}) diff --git a/sync/models/sync_project.py b/sync/models/sync_project.py index be8fe4c1..20c629b0 100644 --- a/sync/models/sync_project.py +++ b/sync/models/sync_project.py @@ -1,4 +1,4 @@ -# Copyright 2020,2022,2024 Ivan Yelizariev +# Copyright 2020,2022,2024-2025 Ivan Yelizariev # Copyright 2020-2021 Denis Mudarisov # Copyright 2021 Ilya Ilchenko # License MIT (https://opensource.org/licenses/MIT). @@ -519,6 +519,12 @@ def link_src_dst(src_data, dst_ref): "sync_external": sync_external, } + def task(self, technical_name): + """Finds task by technical_name""" + return self.task_ids.filtered( + lambda task: task.technical_name == technical_name + )[:1] + def magic_upgrade(self): self.ensure_one() if not self.source_url: @@ -645,6 +651,7 @@ def magic_upgrade(self): task_vals = { "name": task_name, + "technical_name": task_technical_name, "code": file_content, "magic_button": meta.get("MAGIC_BUTTON", "Magic ✨ Button") if has_handle_button diff --git a/sync/models/sync_task.py b/sync/models/sync_task.py index 4f4be285..8a04b63e 100644 --- a/sync/models/sync_task.py +++ b/sync/models/sync_task.py @@ -1,4 +1,4 @@ -# Copyright 2020 Ivan Yelizariev +# Copyright 2020,2025 Ivan Yelizariev # Copyright 2021 Denis Mudarisov # License MIT (https://opensource.org/licenses/MIT). @@ -23,6 +23,11 @@ class SyncTask(models.Model): project_id = fields.Many2one("sync.project", ondelete="cascade") name = fields.Char("Name", help="e.g. Sync Products", required=True) + technical_name = fields.Char( + "Technical Name", + help="Identifier equal to gist file name after removing prefix 'task.' and suffix '.py'", + required=True, + ) code = fields.Text("Code") code_check = fields.Text("Syntax check", store=False, readonly=True) active = fields.Boolean(default=True) diff --git a/sync/models/sync_trigger_automation.py b/sync/models/sync_trigger_automation.py index e211d68d..fe9ffe40 100644 --- a/sync/models/sync_trigger_automation.py +++ b/sync/models/sync_trigger_automation.py @@ -52,7 +52,9 @@ def create(self, vals_list): def start(self, records): if self.active: - self.sync_task_id.start(self, args=(records,), with_delay=True) + sync_job = self.sync_task_id.start(self, args=(records,), with_delay=True) + if records._name == "sync.order": + records.write({"sync_job_id": sync_job.id}) def get_code(self): return ( diff --git a/sync/security/ir.model.access.csv b/sync/security/ir.model.access.csv index fa317b6b..7de65d7d 100644 --- a/sync/security/ir.model.access.csv +++ b/sync/security/ir.model.access.csv @@ -8,6 +8,9 @@ access_sync_task_manager,sync.task manager,model_sync_task,sync_group_manager,1, access_sync_order_user,sync.order user,model_sync_order,sync_group_user,1,0,0,0 access_sync_order_dev,sync.order dev,model_sync_order,sync_group_dev,1,1,1,1 access_sync_order_manager,sync.order manager,model_sync_order,sync_group_manager,1,1,1,1 +access_sync_order_line_user,sync.order_line user,model_sync_order_line,sync_group_user,1,0,0,0 +access_sync_order_line_dev,sync.order_line dev,model_sync_order_line,sync_group_dev,1,1,1,1 +access_sync_order_line_manager,sync.order_line manager,model_sync_order_line,sync_group_manager,1,1,1,1 access_sync_data_user,sync.data user,model_sync_data,sync_group_user,1,0,0,0 access_sync_data_dev,sync.data dev,model_sync_data,sync_group_dev,1,1,1,1 access_sync_data_manager,sync.data manager,model_sync_data,sync_group_manager,1,1,1,1 diff --git a/sync/views/sync_order_views.xml b/sync/views/sync_order_views.xml index d68ecf86..7b18ab2b 100644 --- a/sync/views/sync_order_views.xml +++ b/sync/views/sync_order_views.xml @@ -55,21 +55,35 @@ - + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + +
diff --git a/sync/views/sync_project_views.xml b/sync/views/sync_project_views.xml index d6d41c57..9022ff71 100644 --- a/sync/views/sync_project_views.xml +++ b/sync/views/sync_project_views.xml @@ -178,7 +178,7 @@ />