先说结论
Odoo 的活动展位,最核心的设计不是“记录哪个客户租了哪个摊位”,而是先把展位当成可复制、可统计、可售卖的活动库存。
从 /home/ubuntu/odoo-temp/addons/event_booth/models/event_event.py、event_booth.py、event_booth_category.py,以及 website_event_booth_exhibitor/models/event_booth.py 来看,这条链大概是:
- 用活动模板预制 booth 套餐
- 在具体活动里同步成真实展位库存
- 用 available / unavailable 管理是否还能卖
- 在前台只暴露仍有库存的 booth category
- 展位确认后,再转成 sponsor / exhibitor 展示对象
这意味着 Odoo 的 booth 设计重点,不是先做客户台账,而是先做活动资源管理。
第一层:为什么展位分类不是装饰字段,而是套餐层
event.booth.category 看起来很简单,字段并不多:
namesequencedescription- 图片能力
booth_ids
但它的业务意义一点也不简单。
因为在活动招商里,真正先被售卖的通常不是某个具体坐标,而是某种套餐层级:
- 标准展位
- 高级展位
- VIP 展位
所以分类对象本质上承担了三个职责:
1. 商品化命名
让销售和市场有统一对外话术。
2. 前台展示入口
不同层级可以有不同描述、图片和曝光方式。
3. sponsor/exhibitor 规则来源
在扩展模块里,是否生成 sponsor、对应哪种 sponsor type,都是从 booth category 往下传。
也就是说,booth category 不是“给后台好看一点”,而是招商套餐层。
第二层:为什么活动类型切换时,只替换可售 booth,不碰已售 booth
event.event._compute_event_booth_ids() 这段逻辑非常值得细读。
当活动类型变化时,系统会:
- 删除当前仍
available的 booth - 保留已经不再 available 的 booth
- 再把新活动模板里的 booth 行同步进来
这个动作非常成熟。
因为活动模板更新时,最怕的就是两种错误:
错误一:全量覆盖
如果把已经卖掉的 booth 也一起重置,已经确认的招商结果会被误伤。
错误二:完全不更新
那新模板配置又无法真正落到具体活动上。
Odoo 的处理是只替换“还没卖出去的库存”,保留“已经被业务占用的资源”。
这其实就是电商和票务系统常见的一个原则:
模板可以更新,但不能抹掉已经成交的资源。
对活动团队来说,这一点特别重要。 因为很多大型展会都会在招商过程中不断微调套餐结构,如果系统不能安全同步模板,后台迟早乱套。
第三层:为什么 booth 统计要分 total 和 available
event.event 上有:
event_booth_countevent_booth_count_availableevent_booth_category_available_ids
看起来像普通统计字段,实际上非常关键。
因为招商执行最需要的从来不是“有多少展位”,而是:
- 还有多少能卖
- 还剩哪些档位能卖
如果只看总量,运营会被一种假象误导:
- 总数很多,似乎资源还充足
但实际可能是:
- 标准位全没了
- 只剩高价套餐
- 前台继续展示所有品类,导致客户频繁点到售罄项
而 event_booth_category_available_ids 明确表达了一个非常适合前台的概念:
官网展示应该围绕“还有库存的套餐层”来做,而不是围绕后台全部定义来做。
这能直接减少前台无效咨询。
第四层:为什么展位确认的核心动作是状态变化,而不是写客户字段
event.booth 的状态非常克制:
availableunavailable
很多人会觉得这太简单。 但恰恰因为简单,它才适合作为库存系统的主状态。
对于 booth 来说,最关键的业务问题往往就一个:
- 这个资源还卖不卖得出去?
一旦 booth 被客户占用,它就不该再被当作可售库存。
这时候最重要的不是把各种复杂招商阶段都塞进 booth,而是保持 booth 继续承担“库存位”的职责。
销售漏斗、合同审批、回款进度,可以在 CRM 或销售单里细分。 但 booth 自己应该尽量稳定地代表:
- 是哪一个活动资源
- 是否可售
- 当前由谁占用
这也是一种很典型的 Odoo 风格:
让每个对象只承担最核心的一层语义,不把所有流程都硬堆进一个状态字段。
第五层:为什么确认后要发活动消息,而不是只改状态
_post_confirmation_message() 会在 booth 被确认后往活动消息流里发通知。
这说明 Odoo 把展位确认当成活动级事件,而不是 booth 私有改动。
这件事的价值非常现实:
1. 招商动作进入活动协作上下文
项目经理、运营、销售看到的是同一条事件流,不必各自维护平行台账。
2. 展位确认留痕更清楚
后续追查某个 booth 何时被确认,不会只剩下一次冷冰冰的字段 write。
3. 官网、物料、现场准备可以围绕同一事件启动
一旦 booth 确认,后面的 sponsor 资料收集、官网展示、现场搭建就都可以接上。
活动系统里很多混乱,都来自“后台改了状态,但没人知道那意味着什么”。 Odoo 这里显然不想让 booth 确认成为一个静默动作。
第六层:为什么 sponsor 是确认后生成,而不是一开始就手工建一堆
在 website_event_booth_exhibitor 扩展里,booth 会多出:
use_sponsorsponsor_type_idsponsor_id- 一系列 sponsor 的 related 展示字段
更关键的是 _get_or_create_sponsor() 与 _action_post_confirm():
- 如果 booth category 启用了 sponsor 逻辑
- 且当前 booth 已有 partner
- 确认时就按
partner + sponsor_type + exhibitor_type + event去找 sponsor - 找不到才新建
这个设计特别像真正的招商流程。
因为活动官网上的 sponsor,应该是确认后的展示实体,而不是招商早期所有潜在客户的混合池。
如果 sponsor 从一开始就由人工到处乱建,很快会发生三种问题:
1. 潜在客户和已确认展商混在一起
官网展示和实际成交状态不一致。
2. 同一家企业在不同活动、不同级别下互相串台
品牌资料、赞助等级、活动归属都会混乱。
3. booth 和 sponsor 双方维护,最终不同步
后台库存一套,官网展示一套。
所以 sponsor 最合理的诞生时机,本来就该是在 booth 确认之后。
第七层:为什么 sponsor 字段大量 related,反而更稳
扩展模型里大量 sponsor 信息都不是直接存到 booth,而是 related 到 sponsor_id:
sponsor_namesponsor_emailsponsor_phonesponsor_subtitlesponsor_website_descriptionsponsor_image_512
这代表 Odoo 在主动划清边界:
Booth
负责资源占用与招商执行。
Sponsor
负责品牌展示与官网传播。
这两个对象有关联,但不是同一个层次。
如果把官网展示资料直接堆进 booth:
- 销售一改 booth 联系人,官网信息可能被污染
- 市场同事完善品牌描述时,又会碰到招商执行字段
而 related 的做法把入口和展示主体拆开了:
- booth 是业务入口
- sponsor 是传播主体
这比“所有字段都放在一个表里”稳得多。
实施上最应该守住的三个边界
1. booth 状态是库存状态,不是完整销售漏斗
别把 available / unavailable 强行演化成十几个招商阶段。
2. sponsor 应该来自确认后的 booth,而不是脱链手工维护
否则库存与官网展示迟早分家。
3. 活动模板同步应当只影响未售库存
别让模板更新破坏已成交资源。
最后一句
Odoo booth 机制最值得学的地方,不是“能不能卖展位”,而是它先把展位当成库存和套餐,再把 sponsor 当成确认后的展示实体。
这条顺序一旦反过来,系统就会越来越像一堆联系人备注; 这条顺序守住了,活动招商、官网展示和现场执行才会真正围绕同一套对象运转。
DISCUSSION
评论区