chore(seed): add pro appliance demo data generator

This commit is contained in:
Ruslan Bakiev
2026-02-13 19:00:56 +07:00
parent ff592b812d
commit 7c3dfe0740

View File

@@ -0,0 +1,319 @@
# Run with:
# odoo shell -c /etc/odoo/odoo.conf -d <db_name> < odoo/scripts/generate_pro_appliance_demo.py
from datetime import timedelta
from odoo import fields
SEED_TAG = "[SEED:pro-appliance.repair]"
SOURCE_URL = "https://pro-appliance.repair/"
CONTACTS = [
{"name": "Christina Gomez", "phone": "+1-302-503-0047"},
{"name": "Clinton Parker", "phone": "+1-302-503-0048"},
{"name": "Mike Kreller", "phone": "+1-302-503-0049"},
{"name": "Frank Smith", "phone": "+1-302-503-0050"},
{"name": "Mandy Cekine", "phone": "+1-302-503-0051"},
]
LOCATIONS = [
{
"description": "Newark Downtown, DE 19713",
"latitude": 39.683723,
"longitude": -75.749657,
},
{
"description": "Christiana Area, Newark, DE",
"latitude": 39.678002,
"longitude": -75.659408,
},
{
"description": "Bear Area, New Castle County, DE",
"latitude": 39.629276,
"longitude": -75.658262,
},
]
WORK_TYPES = [
("Clothes Dryer", 90),
("Dishwasher", 120),
("Range", 120),
("Refrigerator", 120),
("Microwave", 90),
]
FALLBACK_MATERIALS = [
("Dryer Belt", "DRY-BELT", 35.0),
("Dishwasher Drain Pump", "DW-PUMP", 72.0),
("Range Igniter", "RANGE-IGN", 48.0),
("Refrigerator Thermistor", "FRIDGE-THRM", 29.0),
("Microwave Door Switch", "MW-SWITCH", 17.0),
]
def ensure_source():
source_model = env["contact.source"].sudo()
source = source_model.search([("name", "=", "pro-appliance.repair Website")], limit=1)
if not source:
source = source_model.create(
{
"name": "pro-appliance.repair Website",
"description": f"Generated from {SOURCE_URL} {SEED_TAG}",
"state": "active",
"active": True,
}
)
return source
def ensure_phone_type():
phone_type = env["dsrpt.communication.type"].sudo().search([("code", "=", "phone")], limit=1)
return phone_type
def ensure_work_types():
model = env["repair.work.type"].sudo()
result = []
for name, duration in WORK_TYPES:
rec = model.search([("name", "=", name)], limit=1)
if not rec:
rec = model.create({"name": name, "duration_min": duration, "active": True})
else:
values = {}
if not rec.duration_min:
values["duration_min"] = duration
if not rec.active:
values["active"] = True
if values:
rec.write(values)
result.append(rec)
return result
def ensure_materials():
material_model = env["repair.material"].sudo()
materials = material_model.search([("state", "=", "active")], order="id", limit=20)
if materials:
return materials
existing_any = material_model.search([], order="id", limit=20)
if existing_any:
for rec in existing_any.filtered(lambda m: m.state != "active"):
rec.action_set_active()
return material_model.search([("state", "=", "active")], order="id", limit=20)
created = material_model.browse()
for name, code, cost in FALLBACK_MATERIALS:
rec = material_model.create(
{
"name": name,
"default_code": code,
"uom_name": "pcs",
"standard_cost": cost,
"state": "active",
"active": True,
}
)
created |= rec
return created
def ensure_technician(work_types):
technician_model = env["repair.technician"].sudo()
zone_model = env["repair.fsm.zone"].sudo()
technician = technician_model.search([("state", "=", "active")], limit=1, order="id")
if technician:
missing_types = work_types - technician.work_type_ids
if missing_types:
technician.write({"work_type_ids": [(4, wt.id) for wt in missing_types]})
return technician
zones = zone_model.search([("state", "=", "active")], limit=3)
technician = technician_model.create(
{
"name": "Alex Pro Appliance",
"state": "active",
"active": True,
"work_type_ids": [(6, 0, work_types.ids)],
"zone_ids": [(6, 0, zones.ids)],
}
)
schedule_model = env["repair.technician.schedule"].sudo()
for day in ["0", "1", "2", "3", "4", "5"]:
schedule_model.create(
{
"technician_id": technician.id,
"day_of_week": day,
"hour_from": 9.0,
"hour_to": 17.0,
}
)
return technician
def ensure_contact_and_address(payload, location, source, phone_type):
contact_model = env["dsrpt.contact"].sudo()
address_model = env["dsrpt.contact.address"].sudo()
comm_model = env["dsrpt.contact.communication"].sudo()
contact = contact_model.search([("name", "=", payload["name"])], limit=1)
if not contact:
contact = contact_model.create(
{
"name": payload["name"],
"status": "has_request",
"source_id": source.id,
"note": f"Imported from {SOURCE_URL} {SEED_TAG}",
}
)
address = address_model.search(
[
("contact_id", "=", contact.id),
("description", "=", location["description"]),
],
limit=1,
)
if not address:
address = address_model.create(
{
"contact_id": contact.id,
"description": location["description"],
"latitude": location["latitude"],
"longitude": location["longitude"],
}
)
if phone_type:
existing_phone = comm_model.search(
[
("contact_id", "=", contact.id),
("communication_type_id", "=", phone_type.id),
("value", "=", payload["phone"]),
],
limit=1,
)
if not existing_phone:
comm_model.create(
{
"contact_id": contact.id,
"communication_type_id": phone_type.id,
"value": payload["phone"],
"is_preferred": True if not contact.communication_ids else False,
}
)
return contact, address
def ensure_work_order(contact, address, work_type, technician, materials, index):
order_model = env["repair.work.order"].sudo()
time_model = env["repair.work.order.time"].sudo()
mat_line_model = env["repair.work.order.material"].sudo()
desc = f"{SEED_TAG} {work_type.name} service request from {SOURCE_URL}"
existing = order_model.search(
[
("contact_id", "=", contact.id),
("work_type_id", "=", work_type.id),
("description", "=", desc),
],
limit=1,
)
if existing:
return existing, False
start_dt = fields.Datetime.now() + timedelta(days=index + 1, hours=index)
duration_min = max(work_type.duration_min or 90, 30)
end_dt = start_dt + timedelta(minutes=duration_min)
order = order_model.create(
{
"contact_id": contact.id,
"contact_address_id": address.id,
"work_type_id": work_type.id,
"description": desc,
"requested_datetime": fields.Datetime.now(),
"scheduled_datetime": start_dt,
"scheduled_end": end_dt,
"slot_day": fields.Date.to_date(start_dt),
"technician_id": technician.id if technician else False,
"state": "assigned" if technician else "confirmed",
}
)
time_model.create(
{
"work_order_id": order.id,
"technician_id": technician.id if technician else False,
"description": f"{work_type.name} diagnostics and repair",
"hours": round(duration_min / 60.0, 2),
}
)
if materials:
mat1 = materials[index % len(materials)]
mat_line_model.create(
{
"work_order_id": order.id,
"material_id": mat1.id,
"qty": 1.0 + (index % 2),
}
)
if len(materials) > 1:
mat2 = materials[(index + 1) % len(materials)]
mat_line_model.create(
{
"work_order_id": order.id,
"material_id": mat2.id,
"qty": 1.0,
}
)
return order, True
def run():
source = ensure_source()
phone_type = ensure_phone_type()
work_types = ensure_work_types()
materials = ensure_materials()
technician = ensure_technician(env["repair.work.type"].sudo().browse([wt.id for wt in work_types]))
created_contacts = 0
created_addresses = 0
created_orders = 0
for idx, payload in enumerate(CONTACTS):
location = LOCATIONS[idx % len(LOCATIONS)]
contact_before = env["dsrpt.contact"].sudo().search_count([("name", "=", payload["name"])])
address_before = env["dsrpt.contact.address"].sudo().search_count(
[("description", "=", location["description"])]
)
contact, address = ensure_contact_and_address(payload, location, source, phone_type)
if contact_before == 0:
created_contacts += 1
if address_before == 0:
created_addresses += 1
work_type = work_types[idx % len(work_types)]
_order, was_created = ensure_work_order(contact, address, work_type, technician, materials, idx)
if was_created:
created_orders += 1
env.cr.commit()
print("=== Pro Appliance demo seed completed ===")
print(f"Source: {SOURCE_URL}")
print(f"Contacts created: {created_contacts}")
print(f"Addresses created: {created_addresses}")
print(f"Work orders created: {created_orders}")
print(f"Materials available for linking: {len(materials)}")
run()