213 lines
7.5 KiB
Python
213 lines
7.5 KiB
Python
import xmlrpc.client
|
|
import psycopg2
|
|
from psycopg2.extras import RealDictCursor
|
|
from flask import Flask, render_template, request, session, redirect, url_for, jsonify
|
|
from datetime import datetime, timedelta
|
|
|
|
app = Flask(__name__)
|
|
# Secret key for sessions
|
|
app.secret_key = 'super_secret_tada_migration_key_change_me_in_prod'
|
|
app.permanent_session_lifetime = timedelta(hours=8)
|
|
|
|
# --- Configurations ---
|
|
# PostgreSQL (Hardcoded as requested)
|
|
DB_HOST = "192.169.0.10"
|
|
DB_PORT = "5432"
|
|
DB_NAME = "postgres"
|
|
DB_USER = "postgres"
|
|
DB_PASS = "Mulut!23Berkomunikasi"
|
|
|
|
# Odoo 19
|
|
ODOO_URL = "http://localhost:1900"
|
|
ODOO_DB = "odoo19_OT_v3"
|
|
ODOO_USER = "admin"
|
|
ODOO_PASS = "admin"
|
|
|
|
def get_db_connection():
|
|
conn = psycopg2.connect(
|
|
host=DB_HOST,
|
|
port=DB_PORT,
|
|
dbname=DB_NAME,
|
|
user=DB_USER,
|
|
password=DB_PASS
|
|
)
|
|
return conn
|
|
|
|
@app.route('/')
|
|
def index():
|
|
if 'employee_id' not in session:
|
|
return redirect(url_for('login'))
|
|
return render_template('index.html', employee_id=session['employee_id'])
|
|
|
|
@app.route('/login', methods=['GET', 'POST'])
|
|
def login():
|
|
if request.method == 'POST':
|
|
employee_id = request.form.get('employee_id')
|
|
if employee_id:
|
|
session.permanent = True
|
|
session['employee_id'] = employee_id
|
|
return redirect(url_for('index'))
|
|
return render_template('login.html')
|
|
|
|
@app.route('/logout')
|
|
def logout():
|
|
session.pop('employee_id', None)
|
|
return redirect(url_for('login'))
|
|
|
|
@app.route('/api/customers')
|
|
def get_customers():
|
|
if 'employee_id' not in session:
|
|
return jsonify({"error": "Unauthorized"}), 401
|
|
|
|
# DataTables parameters
|
|
draw = request.args.get('draw', type=int, default=1)
|
|
start = request.args.get('start', type=int, default=0)
|
|
length = request.args.get('length', type=int, default=10)
|
|
search_value = request.args.get('search[value]', default="")
|
|
|
|
order_column_index = request.args.get('order[0][column]', type=int, default=0)
|
|
order_dir = request.args.get('order[0][dir]', default='asc')
|
|
|
|
columns_map = ['id', 'name', 'phone_number', 'email', 'level', 'point_amount', 'responsible', 'total_spending']
|
|
order_column = columns_map[order_column_index] if order_column_index < len(columns_map) else 'id'
|
|
if order_dir not in ['asc', 'desc']:
|
|
order_dir = 'asc'
|
|
|
|
conn = None
|
|
try:
|
|
conn = get_db_connection()
|
|
cursor = conn.cursor(cursor_factory=RealDictCursor)
|
|
|
|
# Base query
|
|
query = "SELECT * FROM tada_member"
|
|
count_query = "SELECT COUNT(*) FROM tada_member"
|
|
params = []
|
|
|
|
if search_value:
|
|
search_pattern = f"%{search_value}%"
|
|
where_clause = " WHERE name ILIKE %s OR phone_number ILIKE %s OR email ILIKE %s"
|
|
query += where_clause
|
|
count_query += where_clause
|
|
params.extend([search_pattern, search_pattern, search_pattern])
|
|
|
|
# Get total filtered count
|
|
cursor.execute(count_query, params)
|
|
total_filtered = cursor.fetchone()['count']
|
|
|
|
# Total records without filter
|
|
cursor.execute("SELECT COUNT(*) FROM tada_member")
|
|
total_records = cursor.fetchone()['count']
|
|
|
|
# Add pagination and sorting
|
|
query += f" ORDER BY {order_column} {order_dir.upper()} LIMIT %s OFFSET %s"
|
|
params.extend([length, start])
|
|
|
|
cursor.execute(query, params)
|
|
customers = cursor.fetchall()
|
|
|
|
# Convert date fields to string if any to allow JSON serialization
|
|
for row in customers:
|
|
if row.get('birthday'):
|
|
row['birthday'] = str(row['birthday'])
|
|
if row.get('updated_at'):
|
|
row['updated_at'] = str(row['updated_at'])
|
|
|
|
return jsonify({
|
|
"draw": draw,
|
|
"recordsTotal": total_records,
|
|
"recordsFiltered": total_filtered,
|
|
"data": customers
|
|
})
|
|
except Exception as e:
|
|
print(f"Error fetching customers: {e}")
|
|
return jsonify({"error": str(e)}), 500
|
|
finally:
|
|
if conn:
|
|
conn.close()
|
|
|
|
@app.route('/api/migrate/<int:customer_id>', methods=['POST'])
|
|
def migrate_customer(customer_id):
|
|
if 'employee_id' not in session:
|
|
return jsonify({"success": False, "message": "Unauthorized"}), 401
|
|
|
|
employee_id = session['employee_id']
|
|
conn = None
|
|
try:
|
|
# Fetch customer data
|
|
conn = get_db_connection()
|
|
cursor = conn.cursor(cursor_factory=RealDictCursor)
|
|
cursor.execute("SELECT * FROM tada_member WHERE id = %s", (customer_id,))
|
|
customer = cursor.fetchone()
|
|
|
|
if not customer:
|
|
return jsonify({"success": False, "message": "Customer not found."}), 404
|
|
|
|
# Odoo connection
|
|
common = xmlrpc.client.ServerProxy(f'{ODOO_URL}/xmlrpc/2/common')
|
|
uid = common.authenticate(ODOO_DB, ODOO_USER, ODOO_PASS, {})
|
|
|
|
if not uid:
|
|
return jsonify({"success": False, "message": "Failed to authenticate with Odoo."}), 500
|
|
|
|
models = xmlrpc.client.ServerProxy(f'{ODOO_URL}/xmlrpc/2/object')
|
|
|
|
# 1. Find loyalty.program based on level
|
|
level = customer.get('level', '')
|
|
program_ids = models.execute_kw(ODOO_DB, uid, ODOO_PASS, 'loyalty.program', 'search', [[('name', 'ilike', level)]])
|
|
|
|
if not program_ids:
|
|
return jsonify({"success": False, "message": f"Loyalty program containing '{level}' not found in Odoo."}), 404
|
|
|
|
program_id = program_ids[0]
|
|
|
|
# Map gender safely (e.g. 'female' -> 'Female')
|
|
gender_val = customer.get('gender')
|
|
if isinstance(gender_val, str) and gender_val:
|
|
gender_val = gender_val.title()
|
|
|
|
# 2. Create res.partner
|
|
partner_data = {
|
|
'name': customer.get('name', ''),
|
|
'phone': customer.get('phone_number', ''),
|
|
'email': customer.get('email', ''),
|
|
'gender': gender_val or '',
|
|
'birth_date': str(customer.get('birthday', '')) if customer.get('birthday') else False,
|
|
'city': customer.get('city', ''),
|
|
'membership_level_id': program_id,
|
|
'total_spend': float(customer.get('total_spending') or 0.0)
|
|
}
|
|
|
|
# Replace None with False for Odoo XML-RPC compatibility
|
|
partner_data = {k: (v if v is not None else False) for k, v in partner_data.items()}
|
|
|
|
partner_id = models.execute_kw(ODOO_DB, uid, ODOO_PASS, 'res.partner', 'create', [partner_data])
|
|
|
|
# 3. Create loyalty.card
|
|
point_amount = customer.get('point_amount')
|
|
card_data = {
|
|
'partner_id': partner_id,
|
|
'program_id': program_id,
|
|
'points': float(point_amount) if point_amount is not None else 0.0
|
|
}
|
|
|
|
card_id = models.execute_kw(ODOO_DB, uid, ODOO_PASS, 'loyalty.card', 'create', [card_data])
|
|
|
|
# 4. Update tada_member
|
|
now = datetime.now()
|
|
cursor.execute("UPDATE tada_member SET responsible = %s, updated_at = %s WHERE id = %s", (employee_id, now, customer_id))
|
|
conn.commit()
|
|
|
|
return jsonify({"success": True, "message": "Migrasi data berhasil!"})
|
|
|
|
except Exception as e:
|
|
if conn:
|
|
conn.rollback()
|
|
print(f"Migration Error: {e}")
|
|
return jsonify({"success": False, "message": str(e)}), 500
|
|
finally:
|
|
if conn:
|
|
conn.close()
|
|
|
|
if __name__ == '__main__':
|
|
app.run(debug=True, port=5000, host='0.0.0.0')
|