diff --git a/odf_core/SALVAGE.md b/odf_core/SALVAGE.md
new file mode 100644
index 0000000..6581e6c
--- /dev/null
+++ b/odf_core/SALVAGE.md
@@ -0,0 +1,15 @@
+# Provenance & status
+
+This module was **salvaged from the archived `bosd/odoo-data-flow` repo**
+(unmerged PR #11, issue #10) before that repo is deleted, so the work is not lost.
+
+- It was **generated with Jules** and is a **prototype** — treat it as a starting
+ point, not finished code. Expect rework.
+- Roadmap home: this is the "cleaning-DB orchestrator" — PLAN item **5.7**, tracked
+ as [`GetFluvo/addons#1`](https://github.com/GetFluvo/addons/issues/1). It is
+ **Core** (it moves/cleans data), so it lives here in the public addons repo.
+- Not for production Odoo — it targets a disposable staging/cleaning database.
+
+Rework before real use: manifest metadata (author/website still point at the old
+repo), security review, tests, and alignment with the addons-repo conventions
+(pre-commit, ruff, pylint).
diff --git a/odf_core/__init__.py b/odf_core/__init__.py
new file mode 100644
index 0000000..cde864b
--- /dev/null
+++ b/odf_core/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import models
diff --git a/odf_core/__manifest__.py b/odf_core/__manifest__.py
new file mode 100644
index 0000000..56d2463
--- /dev/null
+++ b/odf_core/__manifest__.py
@@ -0,0 +1,15 @@
+{
+ 'name': 'Odoo Data Flow Core',
+ 'version': '18.0.1.0.0',
+ 'author': 'Odoo Data Flow',
+ 'depends': ['base'],
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'views/odf_connection_views.xml',
+ 'views/odf_flow_project_views.xml',
+ 'views/menus.xml',
+ 'data/scheduled_actions.xml',
+ ],
+ 'installable': True,
+ 'application': True,
+}
diff --git a/odf_core/data/scheduled_actions.xml b/odf_core/data/scheduled_actions.xml
new file mode 100644
index 0000000..cd4cabc
--- /dev/null
+++ b/odf_core/data/scheduled_actions.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ ODF: Run Flow Projects
+
+ code
+ model._run_projects()
+
+ 1
+ hours
+ -1
+
+
+
diff --git a/odf_core/models/__init__.py b/odf_core/models/__init__.py
new file mode 100644
index 0000000..f77fec2
--- /dev/null
+++ b/odf_core/models/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+
+from . import odf_connection
+from . import odf_flow_project
diff --git a/odf_core/models/odf_connection.py b/odf_core/models/odf_connection.py
new file mode 100644
index 0000000..0ed79a4
--- /dev/null
+++ b/odf_core/models/odf_connection.py
@@ -0,0 +1,33 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields, api
+import logging
+
+_logger = logging.getLogger(__name__)
+
+class OdfConnection(models.Model):
+ _name = 'odf.connection'
+ _description = 'Odoo Data Flow Connection'
+
+ name = fields.Char(required=True)
+ host = fields.Char(required=True)
+ port = fields.Integer(required=True, default=5432)
+ dbname = fields.Char(required=True)
+ user = fields.Char(required=True)
+ password = fields.Char(password=True)
+
+ def test_connection(self):
+ # This is a placeholder for the actual connection test logic.
+ # For now, it just logs a message.
+ _logger.info(f"Testing connection for {self.name}")
+ # In a real implementation, you would use a library like psycopg2
+ # to attempt a connection and provide feedback to the user.
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'title': 'Connection Test',
+ 'message': 'Connection test functionality is not yet implemented.',
+ 'sticky': False,
+ }
+ }
diff --git a/odf_core/models/odf_flow_project.py b/odf_core/models/odf_flow_project.py
new file mode 100644
index 0000000..2f7aa3e
--- /dev/null
+++ b/odf_core/models/odf_flow_project.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+
+from odoo import models, fields, api
+import logging
+import traceback
+
+_logger = logging.getLogger(__name__)
+
+try:
+ from odoo_data_flow.workflow_runner import run_workflow
+except ImportError:
+ run_workflow = None
+ _logger.warning("The 'odoo-data-flow' library is not installed. Please install it to use ODF Core features.")
+
+
+class OdfFlowProject(models.Model):
+ _name = 'odf.flow.project'
+ _description = 'Odoo Data Flow Project'
+
+ name = fields.Char(required=True)
+ active = fields.Boolean(default=True)
+ source_connection_id = fields.Many2one('odf.connection', required=True)
+ destination_connection_id = fields.Many2one('odf.connection', required=True)
+ flow_file_path = fields.Char(string="Flow File Path", required=True)
+ status = fields.Selection([
+ ('new', 'New'),
+ ('running', 'Running'),
+ ('done', 'Done'),
+ ('failed', 'Failed'),
+ ], default='new', copy=False, tracking=True)
+
+ @api.model
+ def _run_projects(self):
+ """
+ This method is called by a scheduled action to run all active data flow projects.
+ """
+ projects = self.search([('active', '=', True), ('status', '!=', 'running')])
+ for project in projects:
+ project.write({'status': 'running'})
+ self.env.cr.commit()
+
+ try:
+ if not run_workflow:
+ raise ImportError("The 'odoo-data-flow' library is not available.")
+
+ if not project.flow_file_path:
+ raise ValueError(f"Flow file path is not set for project '{project.name}'")
+
+ # The odoo-data-flow library expects a path to a YAML config file.
+ run_workflow(project.flow_file_path)
+
+ project.write({'status': 'done'})
+ self.env.cr.commit()
+
+ except Exception:
+ _logger.error(
+ "Failed to run data flow project '%s'.\n%s",
+ project.name,
+ traceback.format_exc()
+ )
+ project.write({'status': 'failed'})
+ self.env.cr.commit()
diff --git a/odf_core/security/ir.model.access.csv b/odf_core/security/ir.model.access.csv
new file mode 100644
index 0000000..3701ab2
--- /dev/null
+++ b/odf_core/security/ir.model.access.csv
@@ -0,0 +1,3 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_odf_connection,odf.connection access,model_odf_connection,base.group_user,1,1,1,1
+access_odf_flow_project,odf.flow.project access,model_odf_flow_project,base.group_user,1,1,1,1
diff --git a/odf_core/views/menus.xml b/odf_core/views/menus.xml
new file mode 100644
index 0000000..7190560
--- /dev/null
+++ b/odf_core/views/menus.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/odf_core/views/odf_connection_views.xml b/odf_core/views/odf_connection_views.xml
new file mode 100644
index 0000000..dd2b655
--- /dev/null
+++ b/odf_core/views/odf_connection_views.xml
@@ -0,0 +1,46 @@
+
+
+
+
+ odf.connection.form
+ odf.connection
+
+
+
+
+
+
+
+ odf.connection.tree
+ odf.connection
+
+
+
+
+
+
+
+
+
+
+
+
+ Connections
+ odf.connection
+ tree,form
+
+
diff --git a/odf_core/views/odf_flow_project_views.xml b/odf_core/views/odf_flow_project_views.xml
new file mode 100644
index 0000000..d54db31
--- /dev/null
+++ b/odf_core/views/odf_flow_project_views.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ odf.flow.project.form
+ odf.flow.project
+
+
+
+
+
+
+
+ odf.flow.project.tree
+ odf.flow.project
+
+
+
+
+
+
+
+
+
+
+ Flow Projects
+ odf.flow.project
+ tree,form
+
+