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

310 lines
10 KiB
Python

import csv
from proteus import config, Model
from decimal import Decimal
# Configuration
DATABASE_NAME = 'tradon'
CSV_FILE_PATH = r'C:\Users\SylvainDUVERNAY\Open Squared\Production - Documents\TRADON Implementation\ITSA\Reference Data\Loaders\Services.csv' # UPDATE THIS PATH!
# Product configuration
PRODUCT_TYPE = 'service' # Service type products
DEFAULT_CATEGORY = 'Services' # Default category name if not found
DEFAULT_UOM = 'Mt' # Default UOM if not found
def connect_to_tryton():
"""Establish connection to Tryton database"""
print(f"Connecting to Tryton database: {DATABASE_NAME}")
try:
#config.set_trytond(DATABASE_NAME)
config.set_xmlrpc('https://admin:dsproject@itsa.open-squared.tech/tradon/')
print("✓ Connected successfully!\n")
return True
except Exception as e:
print(f"✗ Connection failed: {e}")
return False
def get_or_create_category(category_name):
"""Find or create a product category"""
Category = Model.get('product.category')
# Try to find existing category
categories = Category.find([('name', '=', category_name)])
if categories:
print(f" Found existing category: {category_name}")
return categories[0]
else:
# Create new category
new_category = Category()
new_category.name = category_name
new_category.save()
print(f" ✓ Created new category: {category_name}")
return new_category
def get_uom(uom_name):
"""Find Unit of Measure by name"""
Uom = Model.get('product.uom')
# Try exact match first
uoms = Uom.find([('name', '=', uom_name)])
if uoms:
return uoms[0]
# Try case-insensitive search
all_uoms = Uom.find([])
for uom in all_uoms:
if uom.name.lower() == uom_name.lower():
return uom
# If not found, return Unit (default)
print(f" ⚠ Warning: UOM '{uom_name}' not found, using 'Unit'")
default_uoms = Uom.find([('name', '=', 'Unit')])
if default_uoms:
return default_uoms[0]
# If even Unit is not found, get the first available
all_uoms = Uom.find([])
if all_uoms:
print(f" ⚠ Using first available UOM: {all_uoms[0].name}")
return all_uoms[0]
raise ValueError("No UOM found in database!")
def check_product_exists(code):
"""Check if product with given code already exists"""
Product = Model.get('product.product')
products = Product.find([('code', '=', code)])
return products[0] if products else None
def create_service_product(row, category, uom):
"""Create a new service product"""
Product = Model.get('product.product')
Template = Model.get('product.template')
# Create template first
template = Template()
template.name = row['name']
template.type = PRODUCT_TYPE
template.list_price = Decimal(row['sale_price']) if row['sale_price'] else Decimal('0.00')
template.cost_price = Decimal(row['cost_price']) if row['cost_price'] else Decimal('0.00')
template.default_uom = uom
template.category = category
template.salable = True
template.purchasable = False # Services typically not purchased
if row.get('description'):
template.description = row['description']
template.save()
# Create product variant
product = Product()
product.template = template
product.code = row['code']
product.save()
return product
def import_services(csv_file):
"""Import services from CSV file"""
Product = Model.get('product.product')
imported_count = 0
skipped_count = 0
error_count = 0
errors = []
print(f"{'='*70}")
print(f"Importing service products from: {csv_file}")
print(f"{'='*70}\n")
try:
# Open with utf-8-sig to handle BOM
with open(csv_file, 'r', encoding='utf-8-sig') as file:
reader = csv.DictReader(file)
# Debug: Show detected columns
print(f"Detected columns: {reader.fieldnames}\n")
for row_num, row in enumerate(reader, start=2):
try:
# Clean up values
code = row.get('code', '').strip()
name = row.get('name', '').strip()
category_name = row.get('category', DEFAULT_CATEGORY).strip() or DEFAULT_CATEGORY
uom_name = row.get('uom', DEFAULT_UOM).strip() or DEFAULT_UOM
sale_price = row.get('sale_price', '0.00').strip()
cost_price = row.get('cost_price', '0.00').strip()
description = row.get('description', '').strip()
# Skip empty rows
if not code and not name:
continue
# Validate required fields
if not code or not name:
errors.append(f"Row {row_num}: Missing code or name")
error_count += 1
print(f"✗ Row {row_num}: Missing required fields")
continue
print(f"Processing Row {row_num}: {code} - {name}")
# Check if product already exists
existing_product = check_product_exists(code)
if existing_product:
print(f" ⚠ Product code '{code}' already exists: {existing_product.template.name}")
print(f" Skipping...")
skipped_count += 1
continue
# Get or create category
category = get_or_create_category(category_name)
# Get UOM
uom = get_uom(uom_name)
print(f" Using UOM: {uom.name}")
# Create the product
row_data = {
'code': code,
'name': name,
'sale_price': sale_price,
'cost_price': cost_price,
'description': description
}
product = create_service_product(row_data, category, uom)
print(f" ✓ Created service product: {name}")
print(f" Code: {code}")
print(f" Category: {category.name}")
print(f" Sale Price: {sale_price}")
print(f" Cost Price: {cost_price}")
if description:
print(f" Description: {description[:50]}...")
print()
imported_count += 1
except Exception as e:
error_msg = f"Row {row_num} - {code} ({name}): {str(e)}"
errors.append(error_msg)
error_count += 1
print(f"✗ Error on row {row_num}: {e}\n")
# Summary
print(f"{'='*70}")
print("IMPORT SUMMARY")
print(f"{'='*70}")
print(f"Successfully imported: {imported_count} service products")
print(f"Skipped (already exist): {skipped_count} products")
print(f"Errors: {error_count}")
if errors:
print(f"\nError details:")
for error in errors:
print(f" - {error}")
print(f"\n{'='*70}")
except FileNotFoundError:
print(f"✗ Error: CSV file not found at {csv_file}")
print(f"Please update CSV_FILE_PATH in the script with the correct path.")
except Exception as e:
print(f"✗ Fatal error: {e}")
import traceback
traceback.print_exc()
def verify_import():
"""Verify imported service products"""
Product = Model.get('product.product')
print(f"\n{'='*70}")
print("VERIFICATION - Service Products")
print(f"{'='*70}\n")
# Find all service type products
products = Product.find([('template.type', '=', 'service')])
if products:
print(f"Found {len(products)} service products:\n")
print(f"{'Code':<12} {'Name':<35} {'Category':<20} {'Sale Price':<12}")
print("-" * 80)
for product in products:
code = product.code or 'N/A'
name = product.template.name[:34] if product.template.name else 'N/A'
category = product.template.category.name if product.template.category else 'N/A'
sale_price = f"{product.template.list_price:.2f}" if product.template.list_price else '0.00'
print(f"{code:<12} {name:<35} {category:<20} {sale_price:<12}")
else:
print("No service products found")
print()
def list_available_uoms():
"""List all available UOMs in the database"""
print(f"\n{'='*70}")
print("AVAILABLE UNITS OF MEASURE")
print(f"{'='*70}\n")
Uom = Model.get('product.uom')
uoms = Uom.find([])
if uoms:
print(f"Found {len(uoms)} UOMs:\n")
for uom in uoms:
print(f" - {uom.name} (Symbol: {uom.symbol if hasattr(uom, 'symbol') else 'N/A'})")
else:
print("No UOMs found")
print()
def list_available_categories():
"""List all available product categories"""
print(f"\n{'='*70}")
print("AVAILABLE PRODUCT CATEGORIES")
print(f"{'='*70}\n")
Category = Model.get('product.category')
categories = Category.find([])
if categories:
print(f"Found {len(categories)} categories:\n")
for cat in categories:
print(f" - {cat.name}")
else:
print("No categories found")
print()
def main():
print("="*70)
print("TRYTON SERVICE PRODUCT IMPORT SCRIPT (using Proteus)")
print("="*70)
print()
# Connect to Tryton
if not connect_to_tryton():
return 1
# Optional: List available UOMs and categories
# Uncomment these if you want to see what's available in your database
# list_available_uoms()
# list_available_categories()
# Import service products
import_services(CSV_FILE_PATH)
# Verify import
verify_import()
return 0
if __name__ == '__main__':
exit(main())