Files
Implementation_ITSA/Reference Data/python_project/helpers/migration_mapping.py
AzureAD\SylvainDUVERNAY 078843f991 Initial commit
2026-02-13 14:23:19 +01:00

333 lines
10 KiB
Python

# helpers/migration_mapping.py
from datetime import date
import psycopg2
from psycopg2.extras import execute_values
import logging
logger = logging.getLogger(__name__)
class MigrationMapper:
"""Handle migration mapping records between source system and Tryton"""
def __init__(self, db_config):
"""
Initialize with database configuration
Args:
db_config: dict with keys: host, port, database, user, password
"""
self.db_config = db_config
self.connection = None
def __enter__(self):
"""Context manager entry - establish database connection"""
self.connection = psycopg2.connect(**self.db_config)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit - close database connection"""
if self.connection:
if exc_type is None:
self.connection.commit()
else:
self.connection.rollback()
self.connection.close()
def save_mapping(self, object_type, source_id, tryton_model, tryton_id, recon_key):
"""
Save a single migration mapping record
Args:
object_type: Type of object being migrated (e.g., 'sale_contract')
source_id: ID from source system
tryton_model: Tryton model name (e.g., 'sale.sale')
tryton_id: Tryton record ID
recon_key: Reconciliation key for matching (e.g., contract number)
"""
mappings = [{
'object_type': object_type,
'source_id': source_id,
'tryton_model': tryton_model,
'tryton_id': tryton_id,
'recon_key': recon_key
}]
self.save_mappings_batch(mappings)
def save_mappings_batch(self, mappings):
"""
Save multiple migration mapping records in batch
Args:
mappings: List of dicts with keys:
- object_type: Type of object being migrated
- source_id: ID from source system
- tryton_model: Tryton model name
- tryton_id: Tryton record ID
- recon_key: Reconciliation key for matching
"""
if not mappings:
logger.warning("No mappings to save")
return
cursor = self.connection.cursor()
# Prepare data for batch insert
values = [
(
[mapping['object_type']], # Array with single element
[mapping['source_id']], # Array with single element
[mapping['tryton_model']], # Array with single element
mapping['tryton_id'],
[mapping['recon_key']], # Array with single element
date.today()
)
for mapping in mappings
]
try:
# Use execute_values for efficient batch insert
# ON CONFLICT DO NOTHING prevents duplicates
execute_values(
cursor,
"""
INSERT INTO public.os_migration_mapping
(object_type, source_id, tryton_model, tryton_id, recon_key, write_date)
VALUES %s
ON CONFLICT DO NOTHING
""",
values,
template="(%s, %s, %s, %s, %s, %s)"
)
logger.info(f"Saved {len(mappings)} migration mapping records")
except Exception as e:
logger.error(f"Error saving migration mappings: {e}")
raise
finally:
cursor.close()
def get_tryton_id(self, object_type, source_id):
"""
Retrieve Tryton ID for a given source system ID
Args:
object_type: Type of object
source_id: ID from source system
Returns:
int: Tryton ID or None if not found
"""
cursor = self.connection.cursor()
try:
cursor.execute(
"""
SELECT tryton_id
FROM public.os_migration_mapping
WHERE %s = ANY(object_type)
AND %s = ANY(source_id)
ORDER BY write_date DESC
LIMIT 1
""",
(object_type, source_id)
)
result = cursor.fetchone()
return result[0] if result else None
finally:
cursor.close()
def get_source_id(self, object_type, tryton_id):
"""
Retrieve source system ID for a given Tryton ID
Args:
object_type: Type of object
tryton_id: Tryton record ID
Returns:
str: Source ID or None if not found
"""
cursor = self.connection.cursor()
try:
cursor.execute(
"""
SELECT source_id[1]
FROM public.os_migration_mapping
WHERE %s = ANY(object_type)
AND tryton_id = %s
ORDER BY write_date DESC
LIMIT 1
""",
(object_type, tryton_id)
)
result = cursor.fetchone()
return result[0] if result else None
finally:
cursor.close()
def get_mapping_by_recon_key(self, object_type, recon_key):
"""
Retrieve mapping by reconciliation key
Args:
object_type: Type of object
recon_key: Reconciliation key
Returns:
dict: Mapping record or None if not found
"""
cursor = self.connection.cursor()
try:
cursor.execute(
"""
SELECT
object_type[1] as object_type,
source_id[1] as source_id,
tryton_model[1] as tryton_model,
tryton_id,
recon_key[1] as recon_key,
write_date
FROM public.os_migration_mapping
WHERE %s = ANY(object_type)
AND %s = ANY(recon_key)
ORDER BY write_date DESC
LIMIT 1
""",
(object_type, recon_key)
)
result = cursor.fetchone()
if result:
return {
'object_type': result[0],
'source_id': result[1],
'tryton_model': result[2],
'tryton_id': result[3],
'recon_key': result[4],
'write_date': result[5]
}
return None
finally:
cursor.close()
def delete_mappings_by_source(self, object_type, source_ids):
"""
Delete mappings by source IDs (useful for re-import)
Args:
object_type: Type of object
source_ids: List of source IDs to delete
Returns:
int: Number of records deleted
"""
if not source_ids:
return 0
cursor = self.connection.cursor()
try:
# Delete records where source_id matches any in the list
cursor.execute(
"""
DELETE FROM public.os_migration_mapping
WHERE %s = ANY(object_type)
AND source_id[1] = ANY(%s)
""",
(object_type, source_ids)
)
deleted_count = cursor.rowcount
logger.info(f"Deleted {deleted_count} migration mapping records")
return deleted_count
finally:
cursor.close()
def get_all_mappings(self, object_type=None):
"""
Retrieve all mappings, optionally filtered by object type
Args:
object_type: Optional object type filter
Returns:
list: List of mapping dicts
"""
cursor = self.connection.cursor()
try:
if object_type:
cursor.execute(
"""
SELECT
object_type[1] as object_type,
source_id[1] as source_id,
tryton_model[1] as tryton_model,
tryton_id,
recon_key[1] as recon_key,
write_date
FROM public.os_migration_mapping
WHERE %s = ANY(object_type)
ORDER BY write_date DESC
""",
(object_type,)
)
else:
cursor.execute(
"""
SELECT
object_type[1] as object_type,
source_id[1] as source_id,
tryton_model[1] as tryton_model,
tryton_id,
recon_key[1] as recon_key,
write_date
FROM public.os_migration_mapping
ORDER BY write_date DESC
"""
)
results = cursor.fetchall()
return [
{
'object_type': row[0],
'source_id': row[1],
'tryton_model': row[2],
'tryton_id': row[3],
'recon_key': row[4],
'write_date': row[5]
}
for row in results
]
finally:
cursor.close()
# Simplified standalone function for quick integration
def save_migration_mapping(db_config, object_type, source_id, tryton_model,
tryton_id, recon_key):
"""
Standalone function to save a single migration mapping
Args:
db_config: dict with database connection parameters
object_type: Type of object being migrated
source_id: ID from source system
tryton_model: Tryton model name
tryton_id: Tryton record ID
recon_key: Reconciliation key for matching
"""
with MigrationMapper(db_config) as mapper:
mapper.save_mapping(object_type, source_id, tryton_model, tryton_id, recon_key)