From 2329f8cf14efb80a24ed04f4344028cd0223c11a Mon Sep 17 00:00:00 2001 From: luoweijian <1329394916@qq.com> Date: Tue, 1 Sep 2026 20:03:13 +0800 Subject: [PATCH] commit --- .../issues/01-domain-context.md | 29 + .../issues/02-customer-main-table.md | 42 + .../issues/03-ownership-views.md | 41 + .../04-create-dedup-and-company-lookup.md | 39 + .../issues/05-customer-stage.md | 36 + .../issues/06-contacts-crud.md | 57 + .../issues/07-detail-follow-oplog.md | 53 + .../customer-module/issues/08-transfer.md | 47 + .scratch/customer-module/issues/09-import.md | 36 + .../issues/10-overdue-reminder-rule.md | 42 + .../issues/11-saved-view-integration.md | 37 + .scratch/customer-module/map.md | 78 ++ .../demo-addcustomer.js | 50 + .../demo04-1-customer-subtab.png | Bin 0 -> 52977 bytes .../demo04-2-dialog-open.png | Bin 0 -> 55070 bytes .../demo04-3-search-results.png | Bin 0 -> 57901 bytes .../demo04-4-selected-cascade.png | Bin 0 -> 57067 bytes .../demo04-5-after-add.png | Bin 0 -> 56345 bytes .../opp-customer-dependency/drive-demo04.py | 84 ++ .../issues/01-customer-type-dict-seed.md | 16 + .../issues/02-customer-contacts-stub.md | 21 + .../03-frontend-integration-regression.md | 29 + .../issues/04-demo-frontend-acceptance.md | 35 + .../verify-03-result.json | 60 + .scratch/opp-customer-dependency/verify-03.py | 50 + .../商机前端联调工作量评估.md | 95 ++ .scratch/opportunity-acceptance/boot-run.log | Bin 113752 -> 116130 bytes .../01-stage-template-display-name-unique.md | 13 + ...-stage-display-name-resolution-contract.md | 12 + .../issues/03-board-drop-status-filter.md | 13 + .../issues/04-board-group-by-display-name.md | 15 + .../issues/05-board-integration-tests.md | 14 + .../issues/06-board-api-docs.md | 12 + .../issues/07-code-review.md | 12 + .scratch/opportunity-e2e/demo/index.html | 157 ++- .../opportunity-e2e/demo/shot-12-09-lanes.png | Bin 0 -> 54956 bytes .../demo/shot-12-10-lanes-more.png | Bin 0 -> 78265 bytes .../demo/shot-12-11-create-dialog.png | Bin 0 -> 76218 bytes .../demo/shot-12-12-cust-search.png | Bin 0 -> 82872 bytes .../demo/shot-12-13-create-result.png | Bin 0 -> 72798 bytes .../demo/shot-12-14-detail-after-create.png | Bin 0 -> 72798 bytes .../demo/shot-12-15-testenv-mode.png | Bin 0 -> 39245 bytes .../issues/12-demo-sync-opp-create-fields.md | 53 + .scratch/opportunity-e2e/map.md | 2 + .scratch/opportunity-e2e/selftest-12.py | 142 ++ .scratch/patch_board_stagenode.md | 3 + .scratch/patch_board_viewform.md | 3 + .../crm/base/config/MybatisPlusConfig.java | 5 + .../crm/dict/config/DictDataInitializer.java | 10 +- .../dict/config/DictDataInitializerTest.java | 22 +- crm-opportunity/CONTEXT.md | 6 +- .../controller/OpportunitySubController.java | 7 + .../domain/dto/CustomerContactDTO.java | 31 + .../domain/dto/OpportunityDTO.java | 14 + .../service/IOpportunitySubService.java | 4 + .../impl/OpportunitySubServiceImpl.java | 9 + ...0032-board-groups-by-stage-display-name.md | 17 + logs/crm-app-boot.log | 189 +++ logs/crm-app.log | 1224 +++++++++++++++++ logs/crm-app.log.2026-08-31.0.gz | Bin 0 -> 17620 bytes 60 files changed, 2940 insertions(+), 26 deletions(-) create mode 100644 .scratch/customer-module/issues/01-domain-context.md create mode 100644 .scratch/customer-module/issues/02-customer-main-table.md create mode 100644 .scratch/customer-module/issues/03-ownership-views.md create mode 100644 .scratch/customer-module/issues/04-create-dedup-and-company-lookup.md create mode 100644 .scratch/customer-module/issues/05-customer-stage.md create mode 100644 .scratch/customer-module/issues/06-contacts-crud.md create mode 100644 .scratch/customer-module/issues/07-detail-follow-oplog.md create mode 100644 .scratch/customer-module/issues/08-transfer.md create mode 100644 .scratch/customer-module/issues/09-import.md create mode 100644 .scratch/customer-module/issues/10-overdue-reminder-rule.md create mode 100644 .scratch/customer-module/issues/11-saved-view-integration.md create mode 100644 .scratch/customer-module/map.md create mode 100644 .scratch/opp-customer-dependency/demo-addcustomer.js create mode 100644 .scratch/opp-customer-dependency/demo04-1-customer-subtab.png create mode 100644 .scratch/opp-customer-dependency/demo04-2-dialog-open.png create mode 100644 .scratch/opp-customer-dependency/demo04-3-search-results.png create mode 100644 .scratch/opp-customer-dependency/demo04-4-selected-cascade.png create mode 100644 .scratch/opp-customer-dependency/demo04-5-after-add.png create mode 100644 .scratch/opp-customer-dependency/drive-demo04.py create mode 100644 .scratch/opp-customer-dependency/issues/01-customer-type-dict-seed.md create mode 100644 .scratch/opp-customer-dependency/issues/02-customer-contacts-stub.md create mode 100644 .scratch/opp-customer-dependency/issues/03-frontend-integration-regression.md create mode 100644 .scratch/opp-customer-dependency/issues/04-demo-frontend-acceptance.md create mode 100644 .scratch/opp-customer-dependency/verify-03-result.json create mode 100644 .scratch/opp-customer-dependency/verify-03.py create mode 100644 .scratch/opp-customer-dependency/wayfinder/商机前端联调工作量评估.md create mode 100644 .scratch/opportunity-board-display-name/issues/01-stage-template-display-name-unique.md create mode 100644 .scratch/opportunity-board-display-name/issues/02-stage-display-name-resolution-contract.md create mode 100644 .scratch/opportunity-board-display-name/issues/03-board-drop-status-filter.md create mode 100644 .scratch/opportunity-board-display-name/issues/04-board-group-by-display-name.md create mode 100644 .scratch/opportunity-board-display-name/issues/05-board-integration-tests.md create mode 100644 .scratch/opportunity-board-display-name/issues/06-board-api-docs.md create mode 100644 .scratch/opportunity-board-display-name/issues/07-code-review.md create mode 100644 .scratch/opportunity-e2e/demo/shot-12-09-lanes.png create mode 100644 .scratch/opportunity-e2e/demo/shot-12-10-lanes-more.png create mode 100644 .scratch/opportunity-e2e/demo/shot-12-11-create-dialog.png create mode 100644 .scratch/opportunity-e2e/demo/shot-12-12-cust-search.png create mode 100644 .scratch/opportunity-e2e/demo/shot-12-13-create-result.png create mode 100644 .scratch/opportunity-e2e/demo/shot-12-14-detail-after-create.png create mode 100644 .scratch/opportunity-e2e/demo/shot-12-15-testenv-mode.png create mode 100644 .scratch/opportunity-e2e/issues/12-demo-sync-opp-create-fields.md create mode 100644 .scratch/opportunity-e2e/selftest-12.py create mode 100644 .scratch/patch_board_stagenode.md create mode 100644 .scratch/patch_board_viewform.md create mode 100644 crm-opportunity/src/main/java/com/crm/opportunity/domain/dto/CustomerContactDTO.java create mode 100644 docs/adr/0032-board-groups-by-stage-display-name.md create mode 100644 logs/crm-app-boot.log create mode 100644 logs/crm-app.log create mode 100644 logs/crm-app.log.2026-08-31.0.gz diff --git a/.scratch/customer-module/issues/01-domain-context.md b/.scratch/customer-module/issues/01-domain-context.md new file mode 100644 index 0000000..e6e64a3 --- /dev/null +++ b/.scratch/customer-module/issues/01-domain-context.md @@ -0,0 +1,29 @@ +# 01 — 客户域领域词汇 + crm-customer/CONTEXT.md 骨架 + +Type: task +Status: open +Blocked by: + +## 目标 + +新建 `crm-customer` 模块,落 `crm-customer/CONTEXT.md`(ubiquitous language,纯 glossary,无实现细节),并在根 `CONTEXT-MAP.md` 增一行。对称 `crm-lead/CONTEXT.md` / `crm-opportunity/CONTEXT.md` 的写法。 + +## 要收录的核心术语(随各票 resolve 回填/校准) + +- **客户 (Customer)** — crm-customer 核心聚合根;归属由 `owner_user_id`(销售负责人)+`owner_dept_id`(销售部门快照) 表达,owner 空=在公海。_Avoid_: 线索(转化前)、商机(下游)、联系人。 +- **销售负责人 / 销售部门** — 客户的 owner + 部门快照;抛公海/交割时变更。_Avoid_: 领取人(那是线索术语)、创建人。 +- **协同人** — 客户团队非负责人成员;抛公海清空、交割不自动恢复。 +- **客户阶段 (customer_stage)** — 潜在→重潜→已成交,只前进不回退,外部成功结果驱动,不可手动改。_Avoid_: 客户状态、归属状态。 +- **档案状态 (archive_status)** — 有效/已归档/已被合并,正交于归属。 +- **客户公海** — owner 空的有效客户逻辑视图;无独立池实体(对称商机公海)。 +- **联系人 (customer_contact)** — 归属某客户的独立实体,无独立 ACL,权限继承客户。 +- **人脉关系单位** — 客户下的关系单位名标签集(非关联 CRM 客户)。 +- **客户交割** — 销售离职/调岗时把名下客户批量交给直属总监,再分配;同步变更关联商机/项目负责人。 + +## 依赖方向 + +单向:`crm-opportunity → crm-customer`(商机读客户/建关联,已有 customer_id 契约占位,见商机图「customer_id 契约占位」决策);`crm-customer → crm-rule → (crm-auth / crm-dict / crm-file)`。客户读「关联商机」走查 crm-opportunity(下游反查上游,注意 ADR-0029 循环依赖技术债,同库单体阶段可接受)。 + +## Answer + +_(待 resolve)_ diff --git a/.scratch/customer-module/issues/02-customer-main-table.md b/.scratch/customer-module/issues/02-customer-main-table.md new file mode 100644 index 0000000..b71cd5f --- /dev/null +++ b/.scratch/customer-module/issues/02-customer-main-table.md @@ -0,0 +1,42 @@ +# 02 — 客户主表字段清单 + +Type: research +Status: open +Blocked by: + +## 问题 + +定客户主表 `customer` 的完整字段清单 + 索引 + DataScope 注解。这是扇出最广的票,其他票(三视图/查重/交割/阶段)回填它。 + +## 一手证据(蓝湖缓存 txt) + +- `a4-3-3-1_新增客户信息.txt` — 新增页字段(基本信息/工商信息/跟进人员/关联信息/补充资料五区) +- `a4-4-1_客户详情_客户信息_.txt` — 详情页字段分区(基本信息/关联信息/补充资料/工商信息/系统信息) +- `a4-2-1_客户总览列表_列表视图_.txt` — 列表列 + 字段设置项 + +## 已知字段线索(待核实归类) + +- **系统生成**:客户编号 `customer_no`(KH+日期+序,只读)、创建人/时间、修改人/时间。 +- **基本信息**:客户名称*、客户类型*(字典)、所属地区*(省市区)、行业*(两级树,复用 crm-dict `industry`)、客户星级*(一~五星)、关系星级*(一~五星)、人脉关系单位(标签集)、公司决策人。 +- **归属(轴1)**:`owner_user_id`(销售负责人*)+`owner_dept_id`(销售部门*快照)+协同人(团队子表)。 +- **阶段(轴2)**:`customer_stage`(潜在/重潜/已成交)。 +- **档案状态**:`archive_status`(有效/已归档/已被合并)。 +- **战略协议**(划出):`战略协议等级` 列在列表出现——但战略协议子域划出,主表是否冗余一个等级快照列供列表展示?→ 本票决定(建议:留 `strategic_agreement_level` 快照列供列表,实体本身划出)。 +- **已成交VIP客户等级**:列表有列 → 主表字段还是 A5 驱动?待定(疑似阶段=已成交后的细分,可能留 seam)。 +- **工商信息**(企查查回填):统一社会信用代码(唯一)、法定代表人、成立时间、注册资本、经营范围、人员规模、年产值、公司地址、工商名称。 +- **关联信息**:是否商机勾兑*、总监近期拜访时间、是否子客户*(是→父客户关联必填)、父客户关联(禁自身及下级/防环)、合作系统、不合作原因、是否参加总裁班/产品经理班(+参加人/电话,条件必填)。 +- **补充资料**:备注(≤500)、附件(crm-file)。 + +## 待拍板点(research 中逐一给推荐 + grill) + +1. 战略协议等级 / 已成交VIP等级 是否主表冗余快照列(战略协议实体划出)? +2. 父子客户:自关联 `parent_customer_id` + 防环校验口径。 +3. 客户类型字典分组(`customer_type`)取值:甲方(企业/政府/普教/中高职/高校)… 需列全。 +4. 客户星级/关系星级:字典 or 定长枚举(一~五星)? +5. 冗余快照列(列表免 join):最近跟进时间、关联业务计数(商机/项目)、owner 名快照。 +6. DataScope:`@DataScope(module="customer", ownerColumn="owner_user_id", deptColumn="owner_dept_id")`。 +7. 索引:UNIQUE 统一社会信用代码 / owner+stage / owner_dept+archive_status(公海视图)。 + +## Answer + +_(待 resolve)_ diff --git a/.scratch/customer-module/issues/03-ownership-views.md b/.scratch/customer-module/issues/03-ownership-views.md new file mode 100644 index 0000000..c979bff --- /dev/null +++ b/.scratch/customer-module/issues/03-ownership-views.md @@ -0,0 +1,41 @@ +# 03 — 归属流转 + 三 workspace + 内置视图 + 自定义视图查询口径 + +Type: research +Status: open +Blocked by: 02 + +## 问题 + +定客户模块三个 workspace(客户总览 / 我的客户 / 客户公海)的**数据范围**、**内置视图**、**共用列**、**行内操作**、**自定义视图接入**(复用商机 saved-view 手册)。同时定归属的迁移动作(领取/分配/抛公海/交割换主/归档)在读侧怎么反映。 + +## 一手证据(蓝湖缓存 txt) + +- `a4-1-1_客户公海列表.txt` / `a4-1-1_客户公海列表_列表视图_.txt` / `a4-1-2_客户公海列表_分屏视图_.txt` — 客户公海(普通销售视角=批量领取 + 领导视角=批量分配) +- `a4-2-1_客户总览列表_列表视图_.txt` / `a4-2-1_客户总览列表_分屏视图_.txt` / `a4-2-1_客户总览列表_卡片视图_.txt` — 客户总览(三视图:表格/列表详情/看板;含分配/抛公海/归档) +- `a4-3-1_我的客户列表_分屏视图_.txt` / `a4-3-1_我的客户列表_列表视图_.txt` / `a4-3-1_我的客户列表_卡片视图_.txt` — 我的客户 + +## 已知事实 + +- **三 workspace 数据范围**(对称商机四视图): + - **我的客户** = 当前用户 owner 或作为协同人的有效客户(去重)。 + - **客户总览** = 按 `@DataScope` 覆盖组织范围内的**未删除、未进入公海**客户;含内置子筛「我负责的 / 我协同的 / 我关注的 / 最近访问」;**不混入公海**(公海是独立入口);含**已归档客户 / 已被合并客户**两个专门 tab(原型 A4-2-1 有)。 + - **客户公海** = `archive_status=有效 且 owner_user_id IS NULL` 的逻辑视图;无独立池表;`owner_dept_id`+匹配规则的可见性(对称商机公海口径,待与超期规则票 10 呼应)。 +- **行内操作差异**: + - 我的客户:详情 / 关注 / 跟进 / 编辑;分配/抛公海按 RBAC 出。 + - 客户总览:详情 / 关注 / **分配** / **抛公海** / 更多(含 **归档**);顶部批量:批量分配 / 批量抛公海 / 批量归档 / 工商信息更新。 + - 客户公海:普通销售=详情+关注+**领取**+批量领取;领导=详情+关注+**分配**+批量分配(不做审批)。 +- **三视图模式(仅客户总览有全三种;公海/我的客户只有列表+分屏)**:表格 / 列表详情(左表+右详情) / 看板(客户阶段分列?待原型核实)。切换视图记住用户偏好(复用 crm-preference)。 +- **自定义视图 = crm-preference 平台能力**(商机图票 14 已建平台;本模块新接一个 scope,参商机 `OpportunitySavedViewFilter` 手册)——见票 11。 + +## 待拍板点 + +1. **公海准入规则**(原型 A4-1-1 §3 明说):客户有「进行中商机/项目」不可移入公海——本票拍板:抛公海写路径的前置校验点(读 crm-opportunity 存在性;A5 未建先跳过项目那半)。 +2. **看板视图(卡片视图)分列维度**:客户阶段?客户星级?→ 需 grill 或读原型细节。 +3. **共用列 vs 差异列**:列表统一列 = 客户名/客户类型/行业/客户阶段/星级×2/战略等级/VIP等级/进入公海时间(公海)or销售负责人+部门+关联业务+最近跟进(总览/我的)。字段设置=复用 crm-preference 列偏好。 +4. **归属迁移动作枚举**:领取(公海→个人)、分配(公海→个人 / 个人→个人)、抛公海(个人→公海)、交割(交割票 08 专管,本票只留 seam)、归档(→archive_status=2)。每边写操作日志(票 07)。 +5. **并发**:所有归属变更 CAS 乐观锁 + 版本号;批量操作跳过已被他人处理的(Toast 报成功/跳过数)。 +6. **领导视角 DataScope 映射**:客户总览 = ALL(领导) / DEPT_AND_CHILD(总监) / DEPT(普通销售不可见总览菜单)——对称 crm-auth DataScopeEnum。 + +## Answer + +_(待 resolve)_ diff --git a/.scratch/customer-module/issues/04-create-dedup-and-company-lookup.md b/.scratch/customer-module/issues/04-create-dedup-and-company-lookup.md new file mode 100644 index 0000000..a7bff74 --- /dev/null +++ b/.scratch/customer-module/issues/04-create-dedup-and-company-lookup.md @@ -0,0 +1,39 @@ +# 04 — 新增/编辑客户的查重校验 + 企查查工商回填 seam + +Type: research +Status: open +Blocked by: 02 + +## 问题 + +定新增/编辑客户表单的**查重校验规则**(内建在新增流程、写死不做后台可配)+ **企查查外部服务契约 seam**(调用点/回填范围/异常/精确查重触发)。**A4-6 独立查重页 + 客户合并不做**(chart grill D6)。 + +## 一手证据(蓝湖缓存 txt) + +- `a4-3-3-1_新增客户信息.txt` — 主页面 §3.2 客户名称与自动查重 / §3.3 企业查询与工商信息回填 / §3.4 客户重复校验 / §3.5 联系人明细表 / §3.6 工商信息 +- `a4-3-3-2_编辑客户信息.txt` — 编辑页复用新增结构 + 有 seam + +## 已知事实(原型明文,节选) + +- **自动查重(防抖)**:客户名称停止输入→防抖异步查 CRM 相似客户;仅名称输入框下方展示相似列表;只提示、不阻止;无权限的匹配脱敏为「存在匹配记录」不显 name/编号/负责人。 +- **企业查询**:`查询企业`→调外部工商服务→候选列表→单选回填。首次选择只回填**当前为空且属于允许范围**的工商字段(工商名称/信用代码/法代/成立时间/注册资本/经营范围/人员规模/年产值/工商注册地址)。已填字段再次选择必须弹「受影响字段确认」再覆盖;空值不覆盖原值。**不覆盖**:客户类型/行业/所属地区/关系星级/负责人/部门/协同人/人脉关系单位/联系人/备注。 +- **精确查重触发**:选择企业并回填后立即用统一社会信用代码执行精确查重;保存时再执行一次终查。 +- **保存时校验**(服务端最终查重): + - 统一社会信用代码在**全部未删除客户**中必须唯一 → 完全相同**硬拦禁止创建**(含无权查看时不显 name/编号,只提示阻断)。 + - 客户名称相似但信用代码不同 → 弹「客户相似确认」弹窗,用户可"仍要创建";"仍要创建"后**服务端必须再查一次**(防并发/防中间被人创建同信用代码)。 +- **联系人明细表内联查重**:同表格内联系电话相同→禁止提交定位重复行;无电话按「客户+姓名+职务」提示疑似重复;全库按电话查重(对已存在联系人)。→ 联系人查重口径归本票,联系人 CRUD 建模归票 06。 +- **父子客户防环**:`是否子客户=是`时父客户必填;父客户不得选择自身及其下级 → 服务端必须校验不得形成循环。 +- **人脉关系单位**:标签集(单个≤50字,同 name 不重复),非关联 CRM 客户。 + +## 待拍板点 + +1. **相似客户判定口径**(阈值/算法)——原型说「使用 CRM 后端统一查重服务,不在前端固化阈值」。本票拍板:走什么算法(简单 Trigram / MySQL 全文索引 / 编辑距离 / N-gram 分词)?先给最简可行方案。 +2. **企查查外部服务契约 seam**:不深挖服务实现,只定接口(`CustomerCompanyLookupPort`?)+ 请求(name)/响应(候选列表带9项工商字段)/异常语义(无结果/失败/超时);实现可 mock。 +3. **信用代码格式校验**:18 位数字或字母组合(原型明说,正则拍死)。 +4. **成立时间不得晚于当前日期** 前置校验。 +5. **并发建客户**(同信用代码并发提交):DB 唯一约束兜底 + 乐观返回「重复」错误;先查后写做双保险但不依赖它。 +6. **相似客户提示的脱敏**:无权限用户命中他人客户时的返回负载最小化(不带 name/编号/负责人/联系方式),只回「命中数量+提示语」。 + +## Answer + +_(待 resolve)_ diff --git a/.scratch/customer-module/issues/05-customer-stage.md b/.scratch/customer-module/issues/05-customer-stage.md new file mode 100644 index 0000000..410e58e --- /dev/null +++ b/.scratch/customer-module/issues/05-customer-stage.md @@ -0,0 +1,36 @@ +# 05 — 客户阶段(潜在→重潜→已成交,只前进) + +Type: research +Status: open +Blocked by: 02 + +## 问题 + +定 `customer_stage` 的三态定义、迁移触发源、约束(只前进不回退)、seam 契约(重潜由方案卡驱动,已成交由 A5 驱动)。**本图只做「潜在→重潜」**,已成交留 seam(chart grill D3)。 + +## 一手证据(蓝湖缓存 txt) + +- `a4-4-1_客户详情_客户信息_.txt` §1「客户阶段」明文:新建=潜在;任一方案卡提交并报备成功→重潜;任一项目首次中标赢单→已成交;只前进不回退;方案卡失败/报备失败/商机输单/项目结束**均不触发回退**;阶段条不可点击修改;每次变化刷新公共头部+写操作日志。 + +## 已知事实 + +- 三态:`潜在客户(1) / 重潜客户(2) / 已成交客户(3)`。 +- 默认值:新建客户成功后 = 1 潜在。 +- **驱动源**: + - 1→2:方案卡「提交并报备成功」——方案卡状态机在 crm-opportunity 票 07 已定为 `草稿1/已提交2/已推送3`,本图需在 crm-opportunity 侧添 seam:方案卡状态变 2→3(已推送=报备成功?) 时**回调** customer.promoteStage(customerId, 2)。**需与商机侧核对「报备成功」到底对齐哪个状态**(推送 or 一个新态)。 + - 2→3:A5 项目「首次中标赢单」——A5 未建,留 seam 接口 `CustomerStagePort.promoteToClosed(customerId)`。 +- **只前进**:迁移方法只接受更高阶段;低阶段传入返回 no-op(不报错、不写日志)。 +- **不回退**:方案卡撤回/商机关闭/项目结束**不改阶段**(原型明说)。 +- **不可手动改**:无 setter API 面向前端;仅内部 port + 方案卡回调触发。 + +## 待拍板点 + +1. **重潜的触发点到底是「方案卡提交」还是「方案卡推送/报备成功」**?原型 A4-4-1 说「方案卡提交并报备成功」——「报备」是**商机图 spin-out 的推送链路**(商机图票 07 说推送/报备本期不可达,只有 A5 回调特殊路径可达)。→ 本票拍板:**先按「方案卡状态=已提交(2)」触发重潜**(保守),等推送链路建好再改口径;或等 A5 建好后由「报备成功」驱动。 +2. **seam 接口签名**:`CustomerStagePort.promote(customerId, targetStage)` 单方法 or 两方法(`promoteToRepeat` / `promoteToClosed`)? +3. **幂等**:重复触发(同一方案卡多次触发)→ 只前进逻辑天然幂等,无需去重表。 +4. **父子客户联动**:子客户阶段改变时,父客户是否受影响?→ 推荐:**不联动**(父子各自独立阶段,原型无相关规则)。 +5. **阶段变更写操作日志**(票 07):格式 = 「客户阶段:潜在 → 重潜(由方案卡 XXX 提交触发)」,操作人=「系统」。 + +## Answer + +_(待 resolve)_ diff --git a/.scratch/customer-module/issues/06-contacts-crud.md b/.scratch/customer-module/issues/06-contacts-crud.md new file mode 100644 index 0000000..b101462 --- /dev/null +++ b/.scratch/customer-module/issues/06-contacts-crud.md @@ -0,0 +1,57 @@ +# 06 — 联系人建模 CRUD + 全局搜索 + 商机侧硬依赖接口 + +Type: research +Status: open +Blocked by: 02 + +## 问题 + +定 `customer_contact` 实体表、CRUD 接口、全局搜索(商机侧硬依赖)、联系人电话查重、职务字段占位(图谱划出但字段留占位)。归属客户不孤立、权限继承客户团队。 + +## 一手证据(蓝湖缓存 txt) + +- `a4-3-3-1_新增客户信息.txt` — 新增客户内嵌联系人明细表(序号/*联系人/*职务/联系电话/是否内线/礼品备注/来源/操作),OCR/批量导入按钮划出 +- `a4-5-1_联系人列表_列表视图_.txt` / `a4-5-1_联系人列表_分屏视图_.txt` — 顶级联系人菜单 +- `a4-5-2_新增联系人.txt` / `a4-5-3_编辑联系人.txt` — 独立联系人 CRUD +- `a4-5-4_联系人详情_联系人信息_.txt` / `a4-5-5_联系人详情_联系人信息_.txt` — 联系人详情 +- `a4-5-5_联系人详情_操作日志_.txt` / `a4-5-6_联系人详情_操作日志_.txt` — 联系人操作日志 +- `a4-3-4_添加联系人.txt` — 客户下快速添加联系人 +- `a4-4-2_客户详情_联系人_.txt` — 客户详情联系人页签 +- `.scratch/opp-customer-dependency/商机模块前置客户接口依赖分析报告.md` — 商机硬依赖清单 + +## 已知字段线索 + +- **联系人**(必填)— 姓名。 +- **职务**(必填)— 见 `a4-3-3-1.txt` 底部「职务 - 搜索二级联动下拉框」需求:**图谱划出但存储字段留占位**:`job_title_name`(文本必填) + `job_title_id`(标准职务id可空) + `job_title_category`(类别可空) + `job_title_level`(层级 int 可空)。自定义职务时后三列空。 +- **联系电话** — 手机号格式;**全库按电话查重**:新增/编辑时电话完全相同禁止创建;同表格内不同行电话相同禁止提交。 +- **是否内线** — 布尔。 +- **是否关键联系人** — 布尔(原型有)。 +- **礼品备注** — 文本。 +- **来源** — 字典 `contact_source`:组会/组局/转介绍/其他。 +- **归属客户** — `customer_id` NOT NULL(无孤立联系人)。 +- **系统字段** — 创建人/时间、修改人/时间。 + +## 商机侧硬依赖(对齐 opp-customer-dependency P0/P1) + +| 优先级 | 接口 | 商机侧用途 | +|---|---|---| +| P0 | `POST /api/customer/search` | 商机新增关联客户下拉数据源 | +| P0 | `GET /api/customer/{id}/contacts` | 商机字段17按客户筛选联系人 | +| P0 | `GET /api/customer/{id}` | 商机编辑回显客户带出 | +| P1 | `GET /api/customer/contact/search` | 商机字段13全局搜联系人+带出所属公司 | +| P1 | `POST /api/customer/quick-create` | 商机弹窗「+快速创建客户」入口 | + +**本票产出**:以上契约签名 + 请求/响应 DTO(复用已有的分析报告,把它 promote 成正式契约稿)。 + +## 待拍板点 + +1. **联系人权限模型**:明说「继承客户团队权限」——本票落成契约:无独立 `@DataScope` 注解,服务层显式按 customer 校验(对客户有读权限即对全部联系人有读权限)。 +2. **联系人删除**:软删还是硬删?→ 推荐软删(联系人可能被商机/项目引用做快照);主表加 `deleted` 位。 +3. **联系人电话唯一 vs 允许一号多人**:原型「联系电话完全相同禁止新建」听起来是全库唯一——但同一联系人可能有座机 vs 手机?→ 拍板:**仅手机号做唯一约束**,座机不查重;或直接单一 `phone` 列全库唯一,用户共号自己处理。先按后者简化。 +4. **快速创建客户 `POST /api/customer/quick-create`**:字段集是**新增客户表单的必填最小子集**?还是就用完整表单?→ 推荐最小集(客户名称/客户类型/所属地区/行业/星级/关系星级)+ 服务端默认(销售负责人=当前用户/is_child=否)。 +5. **联系人的操作日志**:单独一张 `customer_contact_oplog` 还是并入客户 oplog(票 07)用 `biz_type=CONTACT`?→ 推荐后者,参商机 oplog 口径。 +6. **人脉关系单位** 与联系人是独立集合,仅是标签(非联系人子表)——归客户主表 JSON/子表(票 02 决定)。 + +## Answer + +_(待 resolve)_ diff --git a/.scratch/customer-module/issues/07-detail-follow-oplog.md b/.scratch/customer-module/issues/07-detail-follow-oplog.md new file mode 100644 index 0000000..6d85522 --- /dev/null +++ b/.scratch/customer-module/issues/07-detail-follow-oplog.md @@ -0,0 +1,53 @@ +# 07 — 客户详情 8 页签 + 跟进记录 + 操作日志 + +Type: research +Status: open +Blocked by: 02, 05 + +## 问题 + +定客户详情页公共头部 + 8 页签数据源、跟进记录 `customer_follow` 子表、操作日志 `customer_oplog` 子表(字段级审计口径,对称商机 oplog 票 09)。 + +## 一手证据(蓝湖缓存 txt) + +- `a4-4-1_客户详情_客户信息_.txt` — 公共头部 + 客户信息页签 + 权限/敏感信息/加载/异常规则(**这是详情页的顶层 spec,务必细读**) +- `a4-4-2_客户详情_联系人_.txt` — 联系人页签(复用票 06 数据源) +- `a4-4-3_客户详情_跟进记录_.txt` — 跟进记录页签 +- `a4-4-4_客户详情_关联商机_.txt` — 关联商机页签(读 crm-opportunity ✅ 已建) +- `a4-4-5_客户详情_关联项目_.txt` — 关联项目页签(**读 A5 未建 → 划出本图**,页签保留骨架显"—") +- `a4-4-6_客户详情_团队成员_.txt` / `a4-4-7_客户详情_团队成员_.txt` — 团队成员页签 +- `a4-4-6_客户详情_战略协议_.txt` — 战略协议页签(战略协议子域划出 → 本图页签保留骨架显"—") +- `a4-4-1_客户详情_已归档客户_.txt` / `a4-4-1_客户详情_已被合并客户_.txt` — 归档/合并态的详情皮肤 +- `a4-4-7_客户详情_操作日志_.txt` / `a4-4-8_客户详情_操作日志_.txt` — 操作日志页签 + +## 8 页签清单 + +1. **客户信息** — 主表字段展开(票 02 字段集),支持折叠区块,「编辑客户信息」跳编辑页;「工商信息更新」→ 企查查回填 seam(票 04 复用)。 +2. **联系人** — 票 06 数据源,`GET /api/customer/{id}/contacts`。 +3. **跟进记录** — 本票新建 `customer_follow` 子表。 +4. **关联商机** — 反查 crm-opportunity(`GET /api/opportunity/by-customer/{customerId}` 需商机侧新增或本票新增反查接口)。 +5. **关联项目** — 划出(A5 未建),页签骨架保留。 +6. **战略协议** — 划出(战略协议子域),页签骨架保留。 +7. **团队成员** — 显示销售负责人 + 协同人;领导可维护。 +8. **操作日志** — 本票新建 `customer_oplog` 子表。 + +## 已知事实 + +- **公共头部**:客户阶段(潜在/重潜/已成交)/客户星级/销售负责人+部门/战略协议等级(快照)/已成交VIP等级/客户档案状态/最近跟进摘要/**汇总卡片**(已中标项目总额=A5读、跟进中方案预估总额=方案卡读、进行中商机总额=crm-opportunity读)/关联业务计数/快捷操作(关注/标记重点客户/跟进/添加联系人/新增商机/新增项目/新增战略协议/工商信息更新)。 +- **敏感信息**:完整/脱敏/无权限三档;脱敏对电话/金额;完整值只当前页面会话生效,刷新恢复脱敏;每次完整查看记录敏感信息查询日志。 +- **加载/异常**:局部区域独立加载/失败/重试;关联模块接口失败只影响该区域,客户基本信息继续展示。 +- **跟进记录**:跟进方式必选(字典 `follow_way` 复用商机)、跟进内容必填(1-500)、下次跟进时间(非必填、不得早于当前);本期**不做**跟进附件;提交后刷新最近跟进+跟进记录页签;商机行也可点跟进复用同一弹窗,自动关联当前客户+该商机(即 `customer_follow.opportunity_id` 可选)。 +- **操作日志**(口径纠偏,对称商机票 09):**字段级审计**——实体(customer/contact/follow/team)/字段/旧值/新值/操作人(系统 vs 人工)/操作时间/来源;**非**动作枚举模型。归属变更(领取/分配/抛公海/交割/归档)/阶段变更/工商信息更新/编辑保存/联系人增删改/团队成员变更/敏感信息查询/仍要创建重复客户/相似客户仍创建 均写日志;未保存/取消/校验失败不写。日志脱敏电话/金额;工商查询 raw / OCR 图片不写日志。默认全记 + 短黑名单排除(非白名单)。 + +## 待拍板点 + +1. **`customer_follow` 表字段**:id/customer_id/opportunity_id(可选)/follow_way/content/next_follow_time/creator/create_time;无草稿态(对称商机票 09);无附件本期。 +2. **`customer_oplog` 表字段**:id/biz_type(customer/contact/follow/team/company_info)/biz_id/entity_field/old_value/new_value/operator_user_id(null=系统)/operator_source/create_time;金额/电话脱敏存原值再回显时脱敏 or 存已脱敏值?→ 推荐存原值+回显时按查看者权限脱敏。 +3. **关联商机反查接口**:本模块调 crm-opportunity 新增 `list-by-customer` 还是 crm-opportunity 直接暴露?→ 推荐 crm-opportunity 侧加,本模块调(对称票 09 的模块边界)。 +4. **公共头部汇总金额脱敏**:无权限=`***万元`;无数据=`0.00万元`;接口失败=`--`+重试(原型明说)。 +5. **关注 / 标记重点客户 = 用户维度个人状态**(对称商机票 14 followed)——本表建 `user_customer_focus`(user_id, customer_id, is_starred)。 +6. **归档/合并态详情皮肤**:只读,隐藏编辑/新增入口;仍展示历史数据。合并态划出(合并不做),归档态本图做。 + +## Answer + +_(待 resolve)_ diff --git a/.scratch/customer-module/issues/08-transfer.md b/.scratch/customer-module/issues/08-transfer.md new file mode 100644 index 0000000..94fa928 --- /dev/null +++ b/.scratch/customer-module/issues/08-transfer.md @@ -0,0 +1,47 @@ +# 08 — 客户交割(跨模块同步商机/项目负责人) + +Type: research +Status: open +Blocked by: 02, 03 + +## 问题 + +定客户交割全流程:发起交接 → 客户分配 → 交接记录 + 详情。核心难点是**跨模块负责人同步变更事务**(客户+关联商机+关联项目一起换主,要么全成要么全不成)。 + +## 一手证据(蓝湖缓存 txt) + +- `a4-7-1_客户交割_发起交接_.txt` — 发起交接页(三 tab 之首)+ 交接原因/预览/确认弹窗 + **公共规则/字段规则/客户及关联业务变更/加载与操作反馈**(关键 spec 段) +- `a4-7-2_客户交割_客户分配_.txt` — 客户分配 tab(接收总监分配到具体销售) +- `a4-7-3_客户交割_交接记录_.txt` — 交接记录 tab +- `a4-7-4_交接记录详情.txt` — 交接记录详情 + +## 已知事实(关键提炼) + +- **三 tab**:发起交接 / 客户分配 / 交接记录(+ 记录详情)。 +- **交接原因**:员工离职 / 岗位调动 / 区域调整(单选必选)。 +- **发起人**:仅销售**本人**可发起本人名下客户交接(不允许主管/管理员代发起)。 +- **接收人**:仅交接人员的**直属销售总监**;员工离职时系统自动定唯一总监,岗位调动/区域调整时手选启用的直属总监。 +- **可交接客户范围**:交接人员**当前为唯一负责人**的**未删除、未进入公海**客户;仅协同人身份的不纳入。 +- **员工离职**:必须交接**全部可交接客户**(无勾选框);**岗位调动/区域调整**:至少勾选1个,跨分页勾选。 +- **发起交接后同步变更**:客户负责人 → 接收总监;**该客户全部关联商机 + 项目负责人同步变更为同一接收人**;协同人保留;商机/项目其他数据不变。 +- **原子性**:客户+商机+项目负责人**全部成功**才算本客户处理成功;不允许出现「客户变了但关联商机未变」的中间态;任一失败整批不生效、不生成正式交接记录、当前表单/搜索条件/勾选保留。 +- **客户分配 tab**:接收总监按交接批次把客户分配给具体销售(单条/批量);分配对象排除交接来源人本人;同步变更客户+关联商机+项目负责人;成功后原负责人(接收总监)移出团队。 +- **未完成交接约束**:同一交接人员有未完成交接记录时不允许再次发起。 +- **交接记录**:唯一交接编号;初始分配状态=待分配,进度=0/N;随分配推进;跟操作日志区分(跨模块自动变更的操作人显示"系统"+注明交接编号)。 + +## 待拍板点 + +1. **跨模块负责人同步的事务边界**: + - **推荐 A(同步事务)**:本地事务包含 customer 表 + 调用 crm-opportunity 的 `batchTransferOwner(oppIds, newOwner)` + 调用 crm-project 的同名接口;任一失败整批回滚。但 crm-project 未建 → 本图只做客户+商机两侧同步,项目侧留 seam(`ProjectOwnerTransferPort`)。 + - **推荐 B(异步 saga)**:客户先变,商机/项目通过消息异步跟;失败重试;对账。 + - 我倾向 **A(同步事务)**:原型明说「不允许出现中间态」→ saga 语义不匹配。同库单体阶段跨模块事务可行(同一 DataSource)。 +2. **`customer_transfer` 表结构**:id/transfer_no/handover_user_id/receiver_dean_id/reason(enum)/remark/status(待分配/进行中/已完成)/total_count/assigned_count/create_time/finish_time。 +3. **`customer_transfer_item` 子表**:id/transfer_id/customer_id/original_owner/new_owner(初始=接收总监)/final_assignee(分配后=最终销售)/status(待分配/已分配/失败)/fail_reason。 +4. **「直属销售总监」怎么定**:读 crm-auth 的 SysDept 树 + 角色配置?→ 需与 crm-auth 现有事实核对,可能加个 `SysUser.superior_user_id` 字段(多角色时用「主直属」)。**本票关键 seam**。 +5. **未完成交接锁**:数据层用 `handover_user_id` 上有一条 status ∈ (待分配/进行中) 的记录做互斥;发起前查一次。 +6. **跳过与部分成功**:客户分配 tab 按客户逐条校验,任一失败不影响其他客户;返回 Toast 成功/失败数 + 逐条失败原因(部分失败不整体回滚)——**与发起交接的强原子性不同**(发起交接=全批同一事务,分配=按客户独立事务)。 +7. **操作日志**:跨模块自动变更操作人显示「系统」+ 注明交接编号(票 07 oplog 字段 `operator_source` 支持 "system+transferNo=XXX")。 + +## Answer + +_(待 resolve)_ diff --git a/.scratch/customer-module/issues/09-import.md b/.scratch/customer-module/issues/09-import.md new file mode 100644 index 0000000..6a88465 --- /dev/null +++ b/.scratch/customer-module/issues/09-import.md @@ -0,0 +1,36 @@ +# 09 — 客户导入 + 联系人快速新增(OCR 划出) + +Type: research +Status: open +Blocked by: 02 + +## 问题 + +定客户 Excel 批量导入流程(模板下载 / 上传解析 / 校验 / 结果反馈)。**OCR 快捷新增划出**(chart D2),本图只做 Excel 导入。 + +## 一手证据(蓝湖缓存 txt) + +- `a4-3-2_客户导入.txt` — 客户导入主页 +- `a4-3-2_新增客户信息.txt` — 与新增页同族(同 a4-3-2 前缀,可能是导入后进入新增回显) +- `a4-3-5_客户导入.txt` / `a4-3-6_客户导入.txt` — 导入步骤/结果页 +- `a4-3-3-3______.txt` / `a4-3-3-4_ocr____.txt` / `a4-3-5_ocr____.txt` — OCR 相关(划出,不读) + +## 已知事实(待原型细读补充) + +- 模板下载 → 用户填 → 上传 → 后台异步解析 → 结果表(成功/失败逐行)。 +- 复用票 04 查重口径:信用代码唯一硬拦、名称相似只提示不阻断(导入场景可能改为「相似=跳过」或「相似=创建但标记」)。 +- 复用票 02 字段清单:必填校验(客户名称/客户类型/所属地区/行业/星级/关系星级)。 +- 销售负责人:允许在 Excel 里指定 or 默认导入者。 + +## 待拍板点 + +1. **导入策略**:全成才提交(整体事务)vs 逐行独立事务(部分成功)?→ 推荐**逐行独立**(对称一般 CRM 导入的常识 + 商机模块导入若已建可参照)。 +2. **异步 vs 同步**:批量大(>500)走异步任务 + 轮询进度 vs 同步阻塞?→ 推荐**同步小批 + 异步大批**双通道,阈值可配。 +3. **相似客户在导入场景的处理**:跳过 / 创建但标记 / 报错 → 拍板。 +4. **导入历史记录表** `customer_import_batch`:批次号 / 上传人 / 上传时间 / 总数 / 成功数 / 失败数 / 状态 / 失败明细文件 url。 +5. **模板字段清单**:Excel 列 = 客户主表可导入字段子集(工商信息、附件、人脉关系单位标签集怎么表达?→ 标签用「;」分隔)。 +6. **联系人一起导入**:Excel 是否含联系人 sheet?还是分两批?→ 推荐只导客户主表,联系人另导(联系人导入本图划出,见票 06)。 + +## Answer + +_(待 resolve)_ diff --git a/.scratch/customer-module/issues/10-overdue-reminder-rule.md b/.scratch/customer-module/issues/10-overdue-reminder-rule.md new file mode 100644 index 0000000..2a8522f --- /dev/null +++ b/.scratch/customer-module/issues/10-overdue-reminder-rule.md @@ -0,0 +1,42 @@ +# 10 — 客户规则族「超期未跟进提醒」(crm-rule 全局单例) + +Type: research +Status: open +Blocked by: + +## 问题 + +在 `crm-rule` 落客户规则族第三个 tab(线索/商机/客户并列),本图**只做「超期未跟进提醒」**一项,**全局单例配置**(不套 V-CONFIG 版本化,chart D4)。「客户查重设置」不做(chart D6)。 + +## 一手证据(蓝湖缓存 txt) + +- `a7-3-3-1_客户管理设置_超期未跟进提醒_.txt` — 全部 spec 都在这里 +- `a7-3-3-2_客户管理设置_客户查重设置_.txt` — **划出不读**(查重规则写死在新增流程,票 04) + +## 已知事实(原型明文) + +- **全局单例**:非按阶段/按部门分别配,仅后台管理面板可见。 +- **顶部总开关**:控制整个超期未跟进提醒功能启用/禁用。 +- **两配置项**: + - **首次触发时间**:客户超过 N 天未跟进→触发首次提醒(默认 30 天);含独立启用开关。 + - **二次提醒间隔时间**:首次后每隔 M 天再提醒(默认 7 天);含独立启用开关。 +- **提醒推送渠道**:钉钉推送 + 应用内通知(对称商机票 05 的通知落点)。 +- **交互**:保存/取消(取消恢复上次保存的配置)。 + +## 待拍板点 + +1. **数据模型**: + - **推荐 A(单行配置表)** `customer_reminder_rule`(id=1 fixed / master_enabled / first_trigger_enabled / first_trigger_days / second_interval_enabled / second_interval_days / updater / update_time)。 + - **推荐 B(键值配置)** 复用某个 platform_config 表按 key 存。 + - 我倾向 **A**(单行表,与商机 V-CONFIG 表族对称位置摆,只是不做版本,操作更直观)。 +2. **触发口径**:「超过 N 天未跟进」=`now - last_valid_follow_time ≥ N`;`last_valid_follow_time` = 客户下最新一条 `customer_follow.create_time`(或字段编辑时间?→ 推荐**仅跟进记录**驱动,字段编辑不算——对齐商机票 05 的 last_valid_follow_time 但商机含字段编辑;这里更保守)。 +3. **计时锚点写入**:`customer.last_valid_follow_time` 冗余到主表(新写跟进时刷新);对称商机主表 `last_valid_follow_time`。→ **回填票 02** 加此字段。 +4. **调度**:定时任务(Quartz / Spring Schedule)每天跑一次;命中的客户写「待发通知」表让通知模块消费(对称商机票 05 的 remind 待发通知)。 +5. **通知去重**:一个客户当天只触发一次(首次或二次,取其一)。 +6. **无 owner 的客户**(在公海)**不参与**超期提醒(对谁提醒?→ 无接收人)。 +7. **禁用后**:定时任务扫到总开关=禁用 → 跳过整轮;启用后从下一轮开始。 +8. **规则生效时机**:chart Q4-补 用户特意强调「规则在客户新增时生效」——我的解读是**新增客户时就把 `last_valid_follow_time` 初始化为 create_time**(客户创建后开始计时,30 天没跟进就首次提醒)。**待和用户确认**这个解读——见 grill 挂号。 + +## Answer + +_(待 resolve)_ diff --git a/.scratch/customer-module/issues/11-saved-view-integration.md b/.scratch/customer-module/issues/11-saved-view-integration.md new file mode 100644 index 0000000..c60fc62 --- /dev/null +++ b/.scratch/customer-module/issues/11-saved-view-integration.md @@ -0,0 +1,37 @@ +# 11 — 自定义视图接入 crm-preference(复用商机 saved-view 平台能力) + +Type: task +Status: open +Blocked by: 03 + +## 目标 + +客户模块的三 workspace(客户总览 / 我的客户 / 客户公海)复用 crm-preference 已建的 `user_saved_view` 平台能力,按接入手册三步走:约定 scope_key + 建 `CustomerSavedViewFilter` + PageParam 加 savedViewId。**平台能力不重写**,商机图票 14 已建。 + +## 已知事实 + +- **平台能力已就绪**(商机图票 14):`user_saved_view` 实体 + `SavedViewService` list/save/delete/setDefault + Controller `/api/preference/view` + 六操作符枚举 `SavedViewOperator`(包含/不包含/属于任一/不属于任一/早于/晚于 等)+ 按 `scope_key` 分组、crm-preference 不校验字段。 +- **商机侧接线样本**:`OpportunitySavedViewFilter`(filter_json → wrapper,字段池白名单 = 票 10 可筛选列,未知字段/操作符跳过)+ `OpportunityPageParam.savedViewId` + ViewQueryImpl 叠加。**本票直接参此样本改客户版**(rule of three,商机是样本1,客户是样本2;样本3出现前不抽共享翻译器)。 +- **原型明说客户支持自定义视图**(`a4-1-1_客户公海列表.txt` / `a4-2-1_客户总览列表.txt` / `a4-3-1_我的客户列表.txt` 三处均有「常用检索 / 自定义检索 / 编辑检索 / 重命名 / 删除」+ 保存对话框条件列表)。 + +## 接入手册三步 + +1. **约定 scope_key**: + - 客户总览 = `customer.overview` + - 我的客户 = `customer.mine` + - 客户公海 = `customer.pool` + - (公海双视角=普通销售/领导 → 是否分两 scope 需拍板;推荐**共用 `customer.pool`**,因为字段池相同,视角只影响行内操作而非筛选。) +2. **建 `CustomerSavedViewFilter`**:filter_json 条件翻译成 MyBatis-Plus wrapper;字段池白名单 = 票 03 定的可筛选列(客户名称/客户类型/行业/客户阶段/星级/关系星级/战略等级/地区/最近跟进时间/进入公海时间 等);未知字段/操作符跳过。 +3. **PageParam 加 `savedViewId`**:客户三 workspace 的 PageParam 均加此字段;ViewQueryImpl 里叠加 filter。 + +## 待拍板点 + +1. **scope_key 数量**:3 个还是把公海/总览合成一个?→ 推荐 3 个(保存的检索按语境分组,用户体验更清晰)。 +2. **公海双视角是否分 scope**:见上,推荐共用。 +3. **排序字段池**:默认可排序 = 创建时间 / 最近跟进时间 / 进入公海时间;其他字段(如客户名称)排不排?→ 参商机的默认口径。 +4. **默认视图**:每个 scope 一个 isDefault 视图,crm-preference 已实现互斥。前端进入 workspace 先调 `/view/list` 取 isDefault 再带 viewId 调 `/page`(对称商机)。 +5. **测试**:本票不写代码,但 spec 需列出接入验收清单(scope_key 唯一/filter 白名单/未知字段跳过/默认视图互斥/端到端 saved-view 可用)。 + +## Answer + +_(待 resolve)_ diff --git a/.scratch/customer-module/map.md b/.scratch/customer-module/map.md new file mode 100644 index 0000000..0a7f78a --- /dev/null +++ b/.scratch/customer-module/map.md @@ -0,0 +1,78 @@ +# Map: 客户模块后端 spec(crm-customer / crm-rule 客户规则) + +`wayfinder:map` + +## Destination + +一份可交接的后端规格说明书(spec),描述"客户(Customer)"域的后端设计,交给实现方(人或 agent)落地——对称商机地图(`.scratch/opportunity-module/`)的产出形态:**spec-only(只出决策 + 契约 + 领域模型,不写实现代码)**,做完后进 `/implement` 阶段。 + +- **`crm-customer`(新建模块)** — 客户业务域:客户主表 + 归属流转(有主/公海 + 交割换主)+ 客户阶段(潜在→重潜→已成交,只前进)+ 三 workspace(客户总览 / 我的客户 / 客户公海)+ 内置视图 + 自定义视图(复用 crm-preference)+ 新增/编辑(含查重校验 + 企查查工商回填 seam)+ 客户导入 + 客户详情 8 页签 + 跟进记录 + 操作日志 + 客户交割(跨模块同步商机/项目负责人)+ 联系人 CRUD + 全局搜索。 +- **`crm-rule`(客户规则族,新增第三族)** — 后台"业务规则"下线索/商机/客户三族并列。客户规则本图**只做「超期未跟进提醒」**(全局单例配置,非版本化)。 + +### 原型出处(蓝湖缓存,已本地提取) + +- 主文档 `bade4454-52aa-44db-8ba2-dec594732ecb`(`【原型】itc信息化业务中台web端 V1.0`)。 +- 截图/文本缓存目录:`D:\code\crm-需求梳理\lanhu-mcp\data\axure_extract_bade4454_screenshots\`(每页 4 文件:`.png`/`.txt`/`_annotations.json`/`_styles.json`;`.txt` 为主)。 +- A4 客户模块页族:`a4-1 客户公海` / `a4-2 客户总览` / `a4-3 我的客户+新增+导入+OCR` / `a4-4 客户详情(8页签)` / `a4-5 联系人` / `a4-6 客户查重+合并` / `a4-7 客户交割`。规则侧 `a7-3-3 客户管理设置`。 +- **前置依赖分析已存在**:`.scratch/opp-customer-dependency/商机模块前置客户接口依赖分析报告.md`(商机侧硬依赖的客户接口:`POST /api/customer/search` / `GET /api/customer/{id}/contacts` / `GET /api/customer/{id}` 等)——客户模块的一个已知锚点切片。 + +## Decisions so far + + + +- **[D1] 目的地形态 = spec-only,对称商机图** — 只产出决策/契约/领域模型,不写实现代码;做完进 /implement。(chart grill Q1) + +- **[D2] 作用域三档划分(chart grill Q2 + Q4 修正)**: + - **① 本图覆盖**:客户核心(实体+查重校验+公海+总览+我的客户+三视图+新增/编辑+导入+详情8页签(关联商机读 crm-opportunity✅)+跟进+操作日志)、联系人(CRUD+全局搜索)、客户交割、客户规则族「超期未跟进提醒」。 + - **② 依赖未建 A5 项目模块 → 留 seam**:客户阶段「已成交客户」(由项目中标驱动)本图只做到「潜在→重潜」;「关联项目」详情页签划出。 + - **③ 自成子域/复杂特性 → 划出本图**(Not-yet / Out-of-scope 记一笔):战略协议(A4-4-9/10)、联系人图谱(职务二级联动+人脉连线)、**客户查重列表+客户合并(A4-6,grill Q4 拍板不做)**、企查查/OCR 外部服务(只定契约 seam,不深挖服务内部;OCR 快捷新增划出)。 + +- **[D3] 客户双轴建模(chart grill Q3)** — 对称商机双轴但两轴都更简单: + - **轴1 归属**:**不做显式 tinyint 状态机**,改用两个正交维度:① `owner_user_id`+`owner_dept_id`(快照) 是否为空 = 有主/在公海(公海=owner 空的逻辑视图,对称商机公海);② `archive_status`(有效1/已归档2/已被合并3) 独立字段,归档/合并是它的迁移。迁移动作 = 领取/分配/抛公海/交割(换主)/归档。 + - **轴2 阶段**:主表 `customer_stage`(潜在1/重潜2/已成交3) 单字段,**只增不减、不可配置**(硬编码三态,非商机版本化模板),由外部成功结果驱动。本图**只做潜在→重潜**(重潜由方案卡报备驱动,crm-opportunity 已建);**已成交留 seam** 给 A5。 + +- **[D4] 客户规则落 crm-rule + 全局单例形态(chart grill Q4)** — 客户规则族作 crm-rule 第三族(线索/商机/客户并列)。本图**只做「超期未跟进提醒」**(全局单例配置:总开关 + 首次触发天数(默认30) + 二次提醒间隔(默认7);定时任务 + 钉钉/应用内通知),**不套 V-CONFIG 版本化**(区别于商机规则)。**「客户查重设置」不做**(A4-6 查重执行侧不做,查重口径写死在新增流程)。 + +- **[D5] 联系人建模(chart grill Q5)** — **联系人 = `crm-customer` 下独立实体表 `customer_contact`**(不单开 crm-contact 模块);每个联系人 `customer_id` 必属一客户(无孤立),权限继承所属客户(联系人无独立 ACL)。**本图做**:联系人 CRUD(列表/详情/新增/编辑)+ 随客户新增内嵌明细 + 全局搜索接口(商机硬依赖)+ 联系人电话查重。**本图不做**:职务二级联动图谱、人脉关系连线、OCR 快捷新增、联系人导入。职务字段先存「职务名称文本 + 可空 标准职务id/类别/层级」三列占位,不实现联动选择器逻辑。 + +- **[D6] 新增客户查重口径(chart grill Q4-补)** — 新增/编辑客户的**查重校验做**、内建新增流程:① 统一社会信用代码全局唯一(硬拦),② 客户名称相似→保存时确认弹窗(可"仍要创建")。查重阈值/口径**写死**,不做后台可配。A4-6 独立查重页 + 客户合并**不做**。 + +## Not yet specified + + + +| 票 | 主题 | 依赖 | 前沿位置 | +|---|---|---|---| +| 01 | 客户域领域词汇 + CONTEXT.md 骨架 (task) | — | ✅ 可开工(无依赖) | +| 02 | **客户主表字段清单** — 扇出最广,多票回填 | — | ✅ 可开工(无依赖) | +| 03 | 归属流转 + 三 workspace + 内置视图查询口径 | 02 | 02 done 后 | +| 04 | 新增查重校验 + 企查查工商回填 seam | 02 | 02 done 后 | +| 05 | 客户阶段(潜在→重潜→已成交,只前进;本图只做前两态) | 02 | 02 done 后 | +| 06 | 联系人 CRUD + 全局搜索 + 商机侧硬依赖接口 | 02 | 02 done 后 | +| 07 | 客户详情 8 页签 + 跟进记录 + 操作日志 | 02, 05 | 02+05 done 后 | +| 08 | 客户交割(跨模块同步商机/项目负责人事务) | 02, 03 | 02+03 done 后 | +| 09 | 客户导入(Excel,OCR 划出) | 02 | 02 done 后 | +| 10 | 客户规则族「超期未跟进提醒」(crm-rule 全局单例) | — | ✅ 可开工(无依赖) | +| 11 | 自定义视图接入 crm-preference(复用商机 saved-view) | 03 | 03 done 后(task) | + +**Frontier(当下可并行开工的票)**:`01`(task,建模块+CONTEXT 骨架)、`02`(主表,扇出最广,优先起 research)、`10`(规则单例,无依赖可并行)。02 resolve 后 03/04/05/06/09 集体解冻,再往下推。 + +**已知 grill 挂号**(不阻塞开票,各票 resolve 时问用户拍板): +- **票 04**:客户名称相似查重的算法/阈值(Trigram / MySQL 全文 / 编辑距离…) +- **票 05**:重潜的触发点到底是「方案卡提交(状态2)」还是「方案卡推送/报备成功(状态3)」? +- **票 08**:直属销售总监怎么定(是否在 SysUser 加 `superior_user_id`)? +- **票 09**:Excel 导入的相似客户处理策略(跳过/创建带标/报错) +- **票 10**:"规则在客户新增时生效"的确认解读——新增客户时初始化 `last_valid_follow_time = create_time`,30 天没跟进首次提醒(本条已在 grill Q4-补 与用户初步对齐,票 resolve 时最终确认) + +## Out of scope + + + +- **战略协议**(A4-4-9/10 等级/新增/编辑)— 独立子域,另 effort。 +- **联系人图谱**(职务二级联动树 + 人脉关系连线 + 层级自动生成)— 复杂可视化,另 effort;本图职务只留占位列。 +- **客户查重列表 + 客户合并**(A4-6-1/2/3)— grill Q4 拍板不做;查重口径写死在新增流程。 +- **OCR 快捷新增**(联系人/客户 OCR 识别)— 外部服务,划出;只在新增流程留 seam。 +- **企查查等工商数据服务内部**— 只定调用契约 seam(调用点/回填范围/异常/精确查重触发),不深挖服务实现。 +- **客户阶段「已成交客户」**(由 A5 项目首次中标驱动)— 留 seam,本图只做潜在→重潜。 +- **「关联项目」详情页签**(读 A5 项目模块)— A5 未建,划出。 +- **联系人导入**— 划出(客户导入本图做,联系人导入随图谱 effort)。 diff --git a/.scratch/opp-customer-dependency/demo-addcustomer.js b/.scratch/opp-customer-dependency/demo-addcustomer.js new file mode 100644 index 0000000..846b3bf --- /dev/null +++ b/.scratch/opp-customer-dependency/demo-addcustomer.js @@ -0,0 +1,50 @@ +/* ---------- 添加关联客户(票04 客户信息区联调:类型筛选/搜索/联系人级联/角色/主要) ---------- */ +let _custPicked = null; +async function dlgAddCustomer() { + _custPicked = null; + openDlg('添加关联客户', ` + ${fld('客户类型(筛选)', ``)} + ${fld('关联客户 *', `
+ +3_T9`}+W&Y>3t7(#sC}=(=y; zzLCCDwzao+;`JT#yci9y<^MA;fSZ9^+cOb~6{}@+2DtT43pg%@g*BI; zE432-tlhoGY?m|`ke~3Uc~MOJT@@}9sDO|9{Lg7Y1TA9155wa2?CvkTgYfe5Pt`tw zfKDZOS2=@xR3evukRGyswEM(V0*>+^FHs*csFam&t=uF@W26o_bi(HKht;o zD5LIC&eA%P CaWu+hw>cs^1Z;KIo3i`MN6A+q}h!yU1n&ARS$GqV~Q`9WUm zeXMg>$&8fC3BDA}AU4hGl=D(fHU3w7s>iJZWj~0|ueW2o;Nm!RRU|Oc-fMe&61Ncz z6)rP3)xAu;dZkE8^Xk!aJmbME7&E4>lVmiIZr=~p27z_f_2H7Eu!x>9ogD*SYcwoo ziWY}RF>IJ(Zt*Kvsc8V6M_DOc@|1ndK>!65W}JGa2tpP;FLrk~9tvBJAIy{i^)3mB z`Nv1EPa1fP?0g+}gPs|wm7Sb725=Iwr=P+(8&d-&OgkYN(9i6L)Fsd2o Ay>kw}Ugv@>0+QQPYmnoD4q8k_9GAbY zpufv5Hh!Fir+O#{wYc?|tYRq=PTJCx%Y0ZOV)2pf29zK#I@Fa&NgMh6OX&tGjOd(d%koFgqerEf=6Yrmxv8K@%q z7Nq>0n*ZhA9$5K$H#`qgzD#}cgiYHwWbvYh9;n;gQ+3=SzZ>X&3zk}S0xBZLGWNbX zh;(n^tvC#axL)#8Lt8RRW4Z>I$!kDhPm%X?jcWPLOEFt_wtz;@>O;FRyC=Bm!-a_S zf84!#+Am-~j6fBq0SSRv`z=^AJJ!o@uIJ34GGc66uL1(u1p6*I>QCa+iDh&8fy~S^ zDvOM8vM?pFj-`c0W*<@yMB(lQc~H=TJ&B18^ta-@t+&@w-TlBQ55M!%j0Ush*vKWc z=K1G|vd=;cK*}?td~`r0Xpzcvr%a&baEl>fzw#ZEB-jAEM;x0U9HgP%u)D3C?{zYu zHDlYTi(Q0hY?6%++O^{=Z9SoXIwe`f_9SIFijKFo=>BZ82mRtd$UC&1F~o}|9_7$f z#K(iXa$5bNt;vZT-x9Vqwzx`DaIcAReoF}l`=w!$TeY4N7z|L<{=tVHk@$~O*x6IR zA^wgPkeBI70zI-36f;c#I?C~*61k*t-tiE$t1%^o4+#YJc$g1-f3q_lj3{H*z1Ygk zV`a;9x(QzZW_4%AXD!l>+KOf8R4pH@D~3TiV=-*aEY2;np0j>HzSN&NIg|U2J|QeQ z;UPo?L-mH8LCu8zMp(-xSDY|TS}#4q@~o1axVdF*<0?m>M+6(%+_l 3)lr6-TO7c#q5x&Jq$!F8-nTo ze0*O{Oj9nikaGQvEVBo3WV~tLr@0+4ssx?YqexN4SM>)}6q2CL_O; z$(YL+n~!Br w02!7u1w zzCEsNQk2&C1Aa~I+X;F~c(fN!C%!vI35pJV9Ls+G8Z0ZZ>bkVM&;f;bA#^JvKHJ~t z^le7chYGZ|yvAG=ZRBD5PWW9Bv2sQx8J6cucR1mRHxA8c1j%gMegXQBe_!DTX_&}W z-7Y3mIQDVu;6chhE2UChFfzGge2`W_F|+IeZn;JaI(qsGJMQ=+cFN|In^`Ou-A}w| z=~VMQhgIytcbU=VZldpK_9KBV{2?CXz6Ui1+j5Wq;e5@Bj Y9lYEN^1tAudQCnU%E5ePr9aqCX( z!7>Mz^8Y~hW3sb-x=9h>sPTs)H!uK?tkXiezibSe=}O|mAC@J-{KY1F{OlaY$)4?s z?EAH{oN*RCyIHu#jD-k( WUO#wA#bI$Q#7~KK&phuF>mi9+hCe)TUP4IVgkQxadAj+5= zm9!5KJ>;p(^=BwGIA$}>8~Mp&U`Gp)zMC)dX?@%}`W7v4&|zMW!}p{nfKD1Ambsc| zR% apWY A%?(kI(kq%IYDE zXByMq6WAT~y23-g%|BsCuUsgT?3%y53eC$WlmH%%3GbA87ZSt+#e6u^uxfn-LFrSK z#j8++^CtMB=DAl3EhrbYw!cvor7U691UYe@EE|8wWsOptO4s&AoR?wwjc!onIe-6l zNL ;BZX)%jA5KGpIY~W% zbF`ezBt>RU?&7Ih*kv~vy|QqG94iXesdw`wwj^>6YRMIhKI=~uS~ofU&^MTeQ`J1z z%%wJqG*j^)M5e#Q9q|KNA*ZR}z=vDqO&dcEdLWbYSg=dp0lA_$>>T_nl#tdZI(^1@ z3Ek8&@lQFK@7n1D*z#1FpNQR%JZQtr*0<$qLZFsE%yWf2R9j(Q-Mg?=L%^&v5#7 ^+XH#Y(DxTm2vW}ERQ54kLJJ3VV&!^xeX~&k#VbI5Q{+o3> zU5V0=MyIaS`*MChi(e#J %0ON`TDA-v#$s5$Q+n Njl%UmGtWHyJ zh1arqGE)cf_DT880?z@7x|&qPuYII6wiXB4y%3OH+8{rgFZf|&k)hi+b~d!&Y*l)G zw3mmhS>PxO&yGBpa40&RpfUG1KpN8+^y68SYbT^>vguEmI}lcJOBl5E=%vS)x*3ew zl2aO^nxa&ZheB>XqvG{kMaRqH=$)(6dX+}PQ1Wct63%z4;ton4ibLlF_S^LxWQg;( zVnUtFGRq8`PL00d6tU }5ia!mG-%cdI zJ1UR=4rCN3)@HZRysU}12%014?;pyhTeN5rk(M=LrCfScc-NC~s|-7f>W0%@(ibFE zOjFM-^EB2C;7jQOi?Ris0%&?zVo`Jg-a8&CSlW{vQnESTY>2^gQqfpyqlt!DdlF;v z^m>f!hJY wP=je`q=eV{;{Z-atKmR+mkI>w(8)m9=Vk) zr7`xs-4|D8Wam}BM8b#Pej$~z4px}=t@H01)vfW;6tU|mDnBOzTVHic*QA!(u8h%1 zbq;quAGs7@a9ZRwFy&=^V@TcfNsm|F#Ut@?;e4I&VYp>>spqTQK^pWn@HbTnNUflT z!y=Qehd9PGgGd>-dUyL4bpNTZKMi?gbdMcc`Ff+Y>kz0&fY3WXbQq&1Zo!rj%VN@X zhkWd)S6Q4#vzlsI(mv3{*+S2AHe{2Cnv_eMwW^D0$+`k?*+Bd2IziIu4BG;Xsge2E zIf^*EE?gGOu#b=fUevfNbDNyv8S;Iy&L96SbhwCgM6hx%n$$%uLq&bwU#h`dnsGF= zWmsPM)*26ek%f13Ko0* qSrZW$vrZ+9FHrZukQ6MUa71oSd8PzYg? zeR8}p{eoj&VrO&N-#(J1y|4Kc9aJMh^g2% w+SDhTckg3#U5>eT6->*g9Ba_j4V84q)J)uZdF=CQrI`0`o*7s$SOZ2+-bob z`aDXZlZ08emM*Bd`}-Ih|KP~58U6iAmOkcUu|iTAvp(TkbV1G6L5^A1pdy}_T&F;+ zyeujiN49C@`V~&bDs>$}KaQLvwG55?oZU_g`u5uQ%~o;=cN<$0SX#U$AZ_x9v^(0D z`buA`AGa9gns6-6Y3^TU=-i|wqThUNo+sS8n8Z7h%g nTd(WyS`@W)F z)t)p!x%U?_%II3gcJ1UIJ8IpW;=VpLu4;S}TBodUmpzxY_mT77O?sxYJ8!LfV1_ON zc`+?j$kkPv>2%nAwYi3nC{N~8=KtuQf;4QxNsQ}nSDGDk=ea^2=G@zo6?@>%iP^m% zDgoO h#i zv`Url0sRoHz~VWM?!gB@Xd?eC7qn=7AXC0PHZwd*fQHB0uu+6etKQyS_eT@s6Y1{x z(o &8;~?iV!t39%7_A5eT;FI}QzduD4d@A4N}?5i$XzTF~E5dhILx^GVd*V+88Y{7n~pR@Lq^E@75 zH=UR-?FA2qzHBTmF(Xip``SE_3rk(u#RHj<0w2zvgX%7Saws?nkR@;2L>~{TN6lhw zoVd_?AHYF#Uw@am3ri>3-9>e6AA!47$5~rMaWAI=jM=>~4Z#V@!hz);`;BSch@q7> zM`9nRi^Zm#tyj#JS0z_Xr;M3_AH?GtmL2-$g3{}#AB7MEIe&}d|M(Psze;=Dp43YG zwc{_LoSkGBh{0KSRBtMT|Jahj$f|y+G}Q6ex66&59yU|X-&8kvK^_VRJ1)R=hOd6% z)`N@{j`OSd+}K_gJ3igLlhCW}_LyxkJIQm{N|cR@(20D+2y%gM-ssXsndK ?E%YB*l;a74TgXfI&O6&Gj+x#WIZfYCSpZ8v;gWY{J zGH6m^QwDySJgiYwosD+a*Nk1|0~|3EbZcT45S)_1=~7>q6W=#MiuDJ8p8`6U>DQ)a z4EAcOosP1KKZEOTjw8u8sBq1Rj>Ua~U|Pcdlnwbc?F!RMuNjSfDj96gXG9M=QUp+o zdHsVTsEZ`&?S-H6!R4X875ST0=*UH&i^pUcj%W8rsm?*z1~6odof3S7DHxFhm@RmD z4usq}V@3UnkB>iCCf#SCZKzExW#YH+--il!akndsx!otT^8ld>y<9{sPK#{qwiiUW zY75qjLi j3_xZ=)Nj? zUV=P=EvlkN +&OI*J3w;0C8cwbPIo-QA3bdRSLtfNX!1=y`!Pt zr=GEI58SQ4vb hmO%;&*nh zfj-nQv8LEdMd)Rb644hVcg1{Htg~eS^tT}XPR-P6&+x9 YkEmcz@$txel=4o#1`sGs+4~BVw;gljgPTb6c+g;Q*;xB|Aj{RLMcrGg zn=YLKu3bX)Ce*Q%Rk`{mnQEj$+MHeej-RN!7`Pyp_PI;YE$$;lh#YFZ-_%UC-9jMi zUDfWRk{oqrE&Wb$t6JmU*Q1S>-D90at_-OxI4S1fW960#;_}xb>+XW0PifO?xfvC` z^K!nm_dJ|Zttu-{EM@qS9I;!HUA_Vh66JV==yJ*RPRPHCp%Y_`^!jEQ24170RWjPg z?)IEmwx%DCX$1xaMZz4@Ek!-`bd&XT*BChqR;?@e3VC>I>QN2b`PWNO_8%cx8ygtx zMKwi5V=gHWd)))#sQmp>fCkr*n%9)L(wi)*B1bEbCVa@J7~5e`8BXcwQl+kjaX9>{ zxL0HMcBkigGMNl&k8A6}*=|U<;!n{50Em_^v*1_O=a} 490|N4?67Kg{g3 zUOn?@YlM|<`&4QWTJq)DTkvH%lfMOhmx008rRm(v?k1Wq!9+37+}yr?Rw`=J#;qNJ zuuEwI4%G|BBb nL%SnVMZw(rVZm^Jy`3N$L<9s%(z4MiWh`Q zhA7wHug|V(et(i%i0q%J6EiVkPx);a2ZJ7Yn{$Xu>Ei4d_i%nX7 {Uuy%cJ?H`wRlE7>0MEhN^|>mlee$1VuH@ zOFM<{^84xRsT|f$j#!O_vtgEK+-=_atn4ELTb{swacdCG_u4$3PQ57)_`PEEG&s8z zFI~rd^Ch0!gjkMiCm FA_O|&kYlV2z0q@i*Y{!J$t(y@YpwpQNUUkCj~)x1qF^$854pVd zr^Mq>w(XkSV*RFTtc#vNxx{)-2%P#%w9|o6kI?XnFI@H#*#-K3k6qz8brE)T3c(Qt zRUfx|PWo1a%~xT&ml%45wngd|887Nz1V9YyOCuVYShi0rvp4II$ED=Zs~LAAP@%`b z>b7Q|&h{l|ZKaH9Ep>Js^~C`a P43%1t6kOD-+4%77 z Zu=ZwBe2M5=Vv-v{@uh`K$G`51AsxOePuNAe8VM`1?<@I zPo{Mw_PtjKywx6<3%VpUZzWcq5*654>nU*=ja~blc+3<{>Q0*|_fT5{cq*xAkNgFJ zdG1?S7j^;Kd3Q1rjC^G<+k#{h%^tXG;T&n|@T8O@YC$pKcV-sXt|XCudP->y>0Ex( z3z1l+ZDLjt%02BZu41be&+_A{5%}*8=^CFJ%$c|K><)Nrt*%S$`!S(3Tz<+lCSHC| zh^n9Jx$jx#aqQJ bVbT449r0t9%1i~Da_}Nh zs>sQ4tS6Rt{f=W^f*5>sDvP`%>E%gHX&GDLPo)@X8khd%S`yD+Kb{`_>;~p6`W3E0 z3(P=X*M^rn;f99_uSOYVY8K0HaKFgT-dC9u60!8Y@05M5JiZp2yMspaXQK;dO8cIM z2~XpA+7$O_^*iZ03devwy1aGn7vuUaayrjvlZm^oqb_ePL^j7&yfl9}(@lN(_EdRN zg$Wn}9+j 6VrGHv`%lcg$Fc2mtf9ha#`SkINfPmd2h zARl^w*}P++?p>@C!Mc*LJXgwG;0@JLqLBZ<~f<#>C_<3TC1+sD;>72QJwM!LJ#R z6-|8;FD_zgzM)~gQOSD35znv~n{JwT=ogTZPIv`j@8K`W_<>jPZjUo##$v*SRIz&m zDj=`+taGD|n1P9_gNLsH{%x6$>}#TPlxh!1(h(s`;-$$QLvk!>5~!74sV&m5*EY#h z ?#%=razN3;1=}>=7PoC%f(I*5O(5r6 zOdRFtazT;nh^L$Pi1+f`)*gZd2S%qJOBku9d%w1BV2TtQ-0LZ8Z81A)rdVJ}) %d!Dp-~2+E^Tm$KcgAs@Oo1EU8_4G8P=-B3U57Tv;pUhUo_ zk (c zB%7MJOnK6-&ID>1*4gVtMXpc5u*K9 |%;0a2&WkAMm_1EzyYmSwwJAxMaS#;oOcI6-=|jQ9sUlN9YKLY*rr^r=dwv z;X!2XbjK6jxtEv9nkC6S@P+qmk1+{KC%62r_Kob5TKHGZ)vB%2Zmpx!W1F=y`^s>~ z!=$vGZiiNehpMUraPA7{r+u24hzWFuJ%X0=MDbX@QK_HXgVK29&MeQUrAhRWy3^*s zD{@6@YLt~aOAoh&a`TC+b)5(CY4UGKZWF~^QqeNH !PIaO5=kT*L#=An z_oNV!@TAc4RIcHl5$&_mFktY@1SEfNdKg{_ N5Uq_jZmF7vN40~Ey}8O5k4Wb{U50rY7x=F7Wb5Uf zJ=}}` o`1Na9o|X z($fIroaa#doe&opI~0Aqb3vS*>9*ThxwaUwe|VZLO$qzclTpuw81g LTJtQb^A z4KA6%Ayqd*gMz9oP;w~oZD3@h&zIU~74=(w$nafGP+b}W$7SykYfeQ%N`#Ll2#rNA zzv?Eo7y6)EzD&HoBMipP60CPgsL5jr0l|{v_97YT;ioQT`JF70^s=U6h z^hBKHCWRq>Rz2PzL#+dyyjd>d$a{Hv<16)^duu(wX3PrEb@1^i#u-)Cv9 $DUF>+babJj7WYqykD+Hp3u qaHh2yyM zR9KBz)z%?^iImUX`AuMap7P}gdQ(M~ #mdwE!q$ho)>Dp7I+#;3cQ|yFr z+8ARU3yO!;k>N|=18%<%%$j40JQb<9s=!Y-Uc9)5_RLN(J33ay#a8$M+M5jwq_A z{tB-iueOQD(#Y&@lQ;ji 7FT7qi!Np 5)` zb&VU}xWIwsb0U<&7{VbtoNYm%-gC#LE+itPK*%fVhKBI-j0%T0J28x^qs=ZoG`Z_} z7OG6q4aAVpa>NZYv-1?*0ymSPjqpkk#ZfSGqqQV1f@F{c2 r%*1#>n3Y;O@;8f}=dz5fzR}X(UQFe2w++|5z`x<@3|qdyS6DRnZz1x( z_3jIR0*?&O_=YbdX0}ra`nD((`$c=Mj>z(;Ro-AC^2UT4`6*EE+!*#{Zek4jmyvQV zWos*Mg>#_Lq-hi0ss6~wcw_53=&<(b6_r*BlXeB%uVDulMSe+4>n425f9f*m(LLbX z@|v1B0ABCUf9edIypVl$gMY|GEk;)-MT62Fr-Y%Kz&FK4(=YzAa3Fd$8|_e|R 4Z&TEi=^?Zc$My>yCO5>ZK8@cy@;x^&G%`bR=CgP9WdvMSH z&-4QTFPL5c?*480|MUm^Ar{@pf4}2<8E oKXg1+a$Xs%n01>q4$IvwUeP{%I6Yfo)M=y*0~0|xJHrV z*N5}PFv~glo#A2np(wkjaRW$5%PDQ#CPE{ZCr5Y_X)$}w6#fc46ce)v`f;(%V_tn+ zZ-Vg=Z8S$RlgzH%l0ReDOJnWGK8l3W3vUzcTjf2rwj9ykxagFljhJmX`VLkh-^e|h z{5xfBXI^gswGjmF*yE(MZf1^5JiZqu+ZXC-Ui*iLLQ)`?6dRmk)o^<-rOx4=H(tQg z2qEta(GI9t+`)vx1GTtv9rw?NTjwx&kMHHD6lq~Ut9np18OMDeE_ l%vm&CViL2Ua8S)?=7#J zx6tE@a3slzgtCKGwZ7J T6oH(^BnNBH5PM#LOqi;eLE(|Ibq1E53Pn;u&kEL1s?ddQZogA;9rY= zdLR3cTKSTlet&-Vo^$q#c2km~!}A;w*+-$oK3fhoZjgEBuUnD%CU*#PV}z$0+Y@g+ z+F>ZTfc(Ub6(e<#1LYhK6qDrXRexyO |B*WEd9i@0Hh8_mvnQk;$kz(J?D@MwKY;{asD3JpWDnozn1% z))OlO9k3{Py=^`1_yF$5N=G=-d6T{i8}Y_};yVpKeVenTC&8+cKW=`e5Wu({V`hE$ z;*;nRQFzM@lZd`dM6xWuX`Jd3;1O`+tYR1?YrK}!$ufbd5(Th-vDY&fB_b?-`Jm{Z zsdxu{{4T&a_~1nd;wp6bmtYV>4~&iT+Kqq&PZ_7AQ!gTCVAt2~UE{BaG?mC)B#n|8 z$(0$+#l3>hRFabde`>8%Tg8KoLc+J1+z8_4#TgBX* ^a%#{U**_!3JdNQ0v^_ zRV1i}VN%IOb?Ht=qocl0zgn(BG*;-!O|-$vduN>;qi?w#7*1)MoS)j^%pNhXr%mu* z@dxrC3~;^!bn#>4u7y2|_8* eC>Fnr8}gO0}vnRD#x+C6`y8xmqf+^k-H9KJ0jJr*wX zjBgT|u(!@W6P+fkQ%&{EjlU4<0m3j7`=$x7Iw|3Av+74(|Dmh`Hl;q303wkEZ^%ML zj=~&PxJJI}SmUwNyOEE@J4egv4dUP5h^6u{=GkW$pxdkfWt})f1|rNJ)HWhU&LCGU z-rGzYdD}gQToQ8Rcb#QM6*z%fhSxI(Lpt=B6v+3WCbJZ `tDzS#EFs-q(H>;_$sjIr3pq(P~V$?%={3BwO&0ZHGy3-nw9ua;rZ^0uCD5 zydIS=tF&}etn5swAT0MWD!o(az(Ey4#bCjU&)lQ0Y%()tUhmNwE~#Vj;BE_%$dh$ZPq!f-GsB7t zonJKkcC~!~0;ZAI*elpg46sbsxglXO8;f$&qn3rlsN>^{kwo#5UGkz6Ri7>_i!Pvz zf)9biygn(+_;wE4cIkRNIaw8he?Od`+jy)1{@ZugHM2#?Gy)#gn3W~PKhvZw&JKjB zN|@1s98s3UWkzLlla4)yiJtwy?Y&+0@C>8LJNaI#fulPax&VEzWE{vhvq>ZET3;YO z&YaZBvV{@f=bgh7$bJhf=LB91`n11NwGBSH`l!npNl+rPDIjN~2v1p$T(}-khR&cY z-hC2RnxezE+mQ)-_?PDUF7n>^!}9jVTZPmKZ@V7bBxXF!I?<~`vqD3)lM{%ePNd!5 zfOj${V3_oAq6QDwfmrS~vK7y&4H4AIo9M*wE#s(-C(+7#`ez1aceO#cwTwyFQ7}$M zu#O}Fx<@`#_X+A@RIn1PFW>H(+EmBL&$$?9R_S(c{-3*v&~6oC<9yaX%j%#0?A@($ zCm9y|;ujkv#o3nBDE#OOvp~$AM>7GsJcwMn;%z*mTUvn8!nydU+ywjKS7%?0GKi1g z%bm7H;4zvJQwWiXyndz%
P%+2bU2)5a5Cootsvs; zQ{P=)GsZrWnzKD^F~2xo+KJeG&IG-?n@V&wsHzLLABKYF{`&^k2l3ECeEkZ!xQb#i zNyFir2^XRhXzlr+{l{cLEBWZ+qSD_c0N`r?`pay-K^=JiL#X4wjbq+3eMaQ8N-t&n z$h#?S-bfo`)UE%(oyUi6_M-m}c6o^~wEjbyz7hY{M~XlH{p0@<9{sls{cm@C&vfx# ze*0u{d&+UXe&+w3<<`efo{YjEu!CFp?d^A?rrx) *HQ(s<2o|3xEu#4W79;!qw=j-XM0Ef5^4~*;ugdAog@3M_e{Xoe zVlp@Cm6qc}g86qf-_@CZwv>^PvE6n%{GxU1;ADI1W+bWq(y(AR+*UrhZFr~6km$>V z`|7&?F!m1|zLDX nWR_< zx48%WJIcWe!0B|u^}uTm9vrnB4_J+(Ry}@f*-wV`pd CtrEF}RAQt-a-zDI+Fg&h++ zH4cJqfzujuhn5-%X{`Neux4!mC!&`8l#;E%3t7jl_}|ouz7yf6R1w>c9#Gg>wqJhB zVE@a{fA{YG;=^*8AC2&67D1z6YU{)N(RT(x3$*fG{WA#jK%`I%pJpwSbcLP$;mXA5 z6`M1eC!4U4AlUWL=Cr3)qvG&XI&x@$d|y6Bd$=E`4Yg=s7mQ9-lcMo;&ePGi%tyvG zVut%f0w @_c z;}!gHI3F+M9J%{&j{dKYz=UA}AV=? +Lh32kKy_aqVX=+t zE1^?ur+bxt_)28QIaX-V`~ z6jjFb@^oYPdFWF_A!FCHMJ&Hx=ZSl8fa>a3YbuAKLNYP7*|(s=i3JY*fvj*WFCR;Y zU-Ib&)V{#m(j+!fjCo^YgVxa3$8zZPA-i?i-D9eEf7X7OwR{-7Jz{~0{oYH+Z5g48 zHGSz?AfD2WD}Gj%sTMyN>R}k_V7(FC7>**-%G;{5Yk0YhtkDT?;#jXmd5k&d(Af0` z5a|^EllBoT5=KyVm2tmX84ci93kHKzbrw{9Z)X=r^#nCs%=9Kz=aq*P##=Jj^+S<1 z?t >{0-rUrRL({?=TH~UA|+o-HxA>jtLBEa?{^}98_g JIym#fBrp!f|FLfVGanKcix(fEzNMT{!U!P-goyow5^5|NwcVF!5m z_XK)Sq{73KpptMvZ^;5VcwF*EC3I){>jScGNh`-AV^7MI&+vv8ZbJ#DQPuu 9@6qi>3{QpWqEHB3ScV=CvtI!(5S8 zk9RZgSHB4#{G#3;IAZg*Bn9khkt_Dae4p9AlqkE+Kv9CxQl94-`4+LP&ZE!}Gqb_a zDlhznQwwvM{ZNkK^iR>)7J&_KVS$K-)eL4~4<(VyJ@XaL);bre%ZG@wCMocG}JE>lt^Y^F5ca zU%M0RzO!a^0xzr@9C7v(v9U09-7SzS%K1GZL1xRUCe;-YZskzC!MVE8OQ~v~97?3a z312y?sK`pyY_yxH96$xQ>DB2MoW!#zPe>`G)0QdNiRrm5P6TQbeKtF!Hi-LtQ)wMz zl>XfWKWrt hJSZARrH8w8Wvxwg;%kNqN>AQQ6m3DgV;c@s0SAV?BBXjZahTPiRtjU6d8B zV)rFYWYnu_V)x2TPia(Kd~@{`TNbOb*LmbM&Ld1G#vR?|q33D0bHp%AyYn|Ia-N>* z$j1kJMz_W^{5FoKrX$W&Q2g9`Jigv;x**Haz)s_~(@CC976I`Pynjr9;wJ5yk1!&8 zB^5@q&dV _9cm==()uo1ApTC q?dU}gr$|*IZPWfPlwmd zK8EnQ?_irnd|fs!yDjL)&94Loq%1G?sy@`4PAwjxMc4--lvbKm)&y>Rb$5I?j{cq& zBjdKI8Q(lhKb*X(g-P=3)(_`U{R^UnkF8<_GsU%{=Jw96>@9=s^*VAuV`WpR$V)(^ z(vW_dNT|mx!}OT)kVdIst~d?b3dSqXuCCFImmPY&KDYQj@oPTteA=*`R#Y0FpjWEM zMrL2$Li b?U!&8~*n%)au8)Qj{XYpLHHlWJX!WfeW zXOW<4rwq@4dDB|Hg@A7l!%!R5CkC=lW1hEFYQtmd$4s+wi)()zgu6||B*KjnE9}Fq z3D(}#k41{9I+yj4M0xZyFgoV-o;({p+pbKjcbAwh^=|#;H@?px7?kV`G-ht{{<*f` z!Oq=Ag-GJybBfSCj}#_9 C3Za3@5x-5TZ|rScaunf(6MLk6l4&8B5Rm`^JiD| zr_FTqW1XFi*YvENUwn`rr;aV2AIh?F`N8eC_~r7tMD-!LDA@Vylg$FEHJzc7JQhjQ z32W2l`1FYQ%l5(|gWTpg54qdFO8Gxlh|G=Y(mPL{u+ O&^5+88TRu?@Zb%q<`4jk{+2i!{37x1c?Yx?28GplOI)svN$o9my( z)7IQo`BrSAaW`0{@=;Z@eVmoA!bOs7<9S07{FWx`BPmOZyp~?r8w26>(kQ7(XW;nO zQ23DMyRWB&H8yqwR{%@&PU+6e?T6( 2VYs@?PMdYYk;Lso(&eM9fB6 z?@0v7pOAN(x=!(@&Siq6rJM*uV5?1OTvgmdZ?Gos{zTw_>6@j}3Kyx7e6P??j Z^ zgXe!}he#s^7G5R4(>glsbYCQ`<)L~e&FntGQlahUU@>~gf~72(q9e%fmkcn+QY?KG z^xHGNJDvVmzN_?TElpS5L21xEbd3O*6q!}JS~)M4+!*f*9Cq|4_L`$L!W@uXUYr%< zt>%B*muS%4SxkjAC+P1}{Qc61-&CXGx ^2%xd_GK8PmV`K?w}% z2ohWO)elYB_-N(pznhfgERHG^FdBg7oZK23P>4qiv$qBGQ0tZuLdOYE`S5hb{wTGS z!+VOf-@ItW2(*JM$Fv#v6Yh8g%@|csx^F$rVXyB@PqQ~_O@)NnRoFz2mQKRUs&~nN zg9~O+A`jT(+);g>)gn^(tK)h?3yCF7rJIGOFqJma`y92R#6;*#UU%Btp`c$@D *|939mq~z5M80y#VVCnY zyWskO=3qF~&lf9U3#WVr;&k#hG)xzIc@y~c*~{#IAA1@`o-TGt%L&HiQHzHvXWM_D zc$RIKV2QMiVjM~;9w<~C8=Url;um+O+Q25KwF_0vjQOI*x& FMB^wUQ9 zX2R}YH}i*}p!jyb5u*c29J>-wLHB4$eut3>iT(!t;&GegBBX($$EAL}_x{Q&H&X(U z^qMS_-;I$#0+g&_iKtinHw)Xa4k7f P}65K83`S8tL`f~nr;uO6d!Z$uD>D^_ywYSp9-@mN;wy@5d6lq&? oYXTqN~Y_$PER0>kiC7SGLS}MB6ymxK% >Ehz|AhCGQ-BPWZu830& zEeCxkeUA^u (muzWYd)hJh3>i(mBQ zN6j1TqeY93*%f=!xY6~R*^m4P-N{1F&KTOWl5S?LX>Vn|%iR7?@SRk))jrcDcbmNQ zj? ^Dz;GXH`pBH^RtgEa07=z~-8TU#v`5xd46`t|U{@XS&IT;uazq!XRM_L#I z_@WQz{VY~bU*>c0rFdM0cF0KY2P|OSlN*%c;$pTE@(q0(kmlxj)hJr?rYfFY?gL{Q zqQz lzL`dAvz76!Gsd zOqCimolNTW5}cnD-P}#1(#7l+q3|r5$CDGBoml(%Jy`g?7kQnX8e)$-C6Dy|*00;c z>@SkCCP2iN31zvd*~scvA2)8Ob~l#k)Y!m!6wi)^bM(1+`0KKl0wyUYKSUb10@zo* z@U8pA05V=L0P%$OgOhI3)xq&+^HPBq!I^4Z%Kp7w-4=68Cbt2!W2IR;g-N2Ev)TZi z8T|~HOjA>3 qdOP`)hR|3dqA6N!q-B{PzjmDM);d+6Ct6X{E?+dy*j zLbFMe4U#dU!hXUWud@D0HA#cVATznH_4(ac(a37KkEr*8_0qN>{mzJNjk$r(%qoXl zhbMxvvVwHjkC5^x?Yby2p;MW|t&?xEH^=5u@?ZnxQ(&lZD&XNY4d4jzNKPl3#xZe} z7aW-J+-`g)r{7{5d-@jN2dhdtVvOgnR3WHg+*F+Eo`0((=X-(3=%Btr;{&^N0S5(B zKFy%Igzrf<^6oSt3M4ldQoru&)G|?(@B@n7ruALc!n!D{!SGf>C#7F(-mJEFiQR~8 z)M%qgf1M0Ibt1?#y7%6|0Z&x1B!`bLaRnlzK=hNpKZBb%awfHcW6CD!gxMfLukcE3 z=<;{M6nk8OX<=z&gJzL!Z6C2)k;_Fxb$VBt?ics!1*IT2W&~0;EyNW(aG9G=mk2$! zm@U+paBM9o;$l+zG1kmBJ18b%Gx-&<%~dyE}md2pZhoY20btrEzx)?w&L{&F`G^&An4!&CIR3 zGy9(|s=7C8uim@Y`@D~=ntE)4O>(NntQhn-xVP;UZ=(-<4Fd$()W{g1f!|6Q$-36v z(2nIxO=UmoZNGQ`V?-KW{+VtL{|ML6z_vX~9q3xySDVLRqoo<>u&_Taw%&qByXwc~ za9+STrxAi-$t+`oKa$Z^gJ8Vf4GU+fJOXa8Z~(#M^LDiQie1<9DO%Mu!aeZk{mzBL zB#n8_X+FVW!0dE23j1^_|K~HGvU}v+X)-V-E6`oAC1+k?dfK9=I#!1ENIS1UT){>M zLk?T1@~km3-T8~-w0?5DC}2&~Vn=V5+$*o7!i=|t54FV)i65r$M_Y%X1QqJ6Hf_^4 zG#09;@VjCPAy18PGoe;x;^1&COMe6U3^CIe!m #>P0UF zAhuN5)L^j?P%rj(#sXHtKv}lqwE}5!4u+zyA)c0gisRwZG$4u;#=11mj{~j%=*x=2 zLaH6sm&sj=I9%tOry&ELJouX@6gF^G03glf5C#oSd`~zS;q{(rq*_vHB$2STW3q$; zkQkhzrdhnZTy3LN>ZnAeyvXE%&Xe!P&TDy>tx+(OR+x+8JaW+_LTBh{V%{I7-9%@K z4Jx>DLRH_N*Nl%5&o{DETMtdL1r{kJXorc%Rpu|Hr*v!bH(61$Q0b^t4^RmWbuQ$- zU#9AT#T0`|t74XglgHMH_-8nCL}6}1^C&x%&wYGTWz#?!YIq#ZxUl<~gp(!BlvjVd zdOi3l(qA>bxB{(7kDQhMWEGn+^%DZ~Ne-t9(D+^y#8nfauKjYRPaK0)XxV6Jf31=} zN@6>_h5Zzu#MRK{GDG!KY;16S*0isnQ9-3C(Ts5ce1GcZrfuUWALdb8veXw{uUeSA zJ(Q aQ_}!LD6sTvC;=g`m~)7`S!2SI1(*GF0(byjoz)VU%bjjadQ_NV$?MXprd3`14s- ze+?y)cTDEXa6XsXxazVHKE6glRVZsHx`2xs_vl@ONvv?YSIqRZ7dD)v%=5J>D^lX6 z5kSjx+ +MFItj8HvBMPZ7jglYNDMQirc!4^_PQ zK+5+`U8p`QMy%9F%j_z-w8_nqoUFz;BHdYnwyI)&ICuiECYIXAl9sj|-voH(S6>4o zYvNiqcRMi1VTq%$l4IO#ga4S0H-a*#QATCn58%N<3l0~S65n`I*)H#Fk4U2_O)c~) zM3zAo-jyo|dY`WIZ43|khhK5tJ)*1${ZJze&wA?hvy=#ffc+n8n3=B&^YC%B^L`-Q z%r7NQVTM)*`4iJYkckh6@T8=`0yNjV-S_BHKG=R58}zE~+KrK`;urCxDt#F7st&d4 zXaRZ*0pp}lnIZn#vbPz8?@&?g%3C|KfN~dgY9$SPR)%z%&Ql^D`Fz{yZcz30%fB7^ zw+>1@CZjnK8P@7;o`&h4vE{gcI~aha7Z@04CSza#yS{OVR|mU#pmo(lsW~P*$HOvC z#68VcKD*~{NnHo^Uy+jQ#+tyLUkN{vxN^CeQ+mZVmIQ;iQPCh35fvir#n-#6E=k*% z6f3$yHi|&}qwSMkHmK<>lqHVhlINNRx8$6N%Gwgtv{i+kxIL
Q3}xZuNAwo(RfjHNN-;&6Aaa;7YfG9)!|lc8owd}9iU z_5O9`O#KiSl6U~%W02DHT<^%2P#j+=T&6ETwf>+#lnxRFRSP!u*4)BAzaCX{1e3lP zkVr8}J-8;q-c@P+BqKb%ha*9i&CGTYR2U-b>3bZ*`STs%;EXlt7A8bhi_wE1ZA0m$ ziECY;j@XC2D0-DQWHD76-#pT#XzS{>6EzM$XltCp*Ba%d&D91lE>x5@Ps&37I37#y z|AkexZ*2*l6?5xFgwrsxGHa%#9&UXZ@(TZvkKMCV3_(D@{fudNV^1aVd=5hLuh9%R2SQ)@*N?VV- zIUq^aJU#|YH9B{t1Jd;7UbeJkvMPL>Gs9$*f(31{vU1uh)l4`v`WN@1{yU^M-@QEZ z;-LHIJQ0b7Sx-1*`S he1>~ba6XKk>wkQ zlyK;ar=TxU(I$_peFR282Q<4Jl-92}z*tZKElCMT?2J$N0ePYQ+q2t0CrW@da$p1k z1_1u4{zZA*PW1>;@c>Q)OrJ`~Z~-VS>0a1RiUK#YvCL2U2n!Pni?y{ikIhnhbrgUf zAHmz|>iyK!#d>%NXd(A`(&~8xeffrX$%G(SM8qrN_ sKOg?@vC{wW#;i@( zS{ir?DISP9*MDh( |fi^^X`8vr|+Dxjj|3=|FW|2ur3$=L%WA? zRmL%)m%>;8FR98vUl37zDNdXl6cklL>b6WH;vabvGWZ^c8R3yNS6h&nB5DKNiSlmu z>PC#RRhQbxL7%fLA`>^L;3Z?pQrwh!3obRmFImVzQ75;CmiTibV^wLEL#s3b@i>3B z$+CW=Z)q0@JVI2^tmeL77^XqwtpaO+{b`(2qxbIU1wEhK^7we;@6I^m1Yy?g#v^@^ zI#{CH$Q%W<<<;%xck7yVTHczg4z1*AI`T2JBEsa9S8cMc?_l!5@x!rQ_3DmK9zHQ6 zGDi4P2?0yQT%LPX^BV?Q&IRE4?vntP?eOHJIH5@ouDeq8(%z8ej~R)Xp@wiFq~Q%x z2ix;5SfFomeU?$;YwlY)1gJQlhPJ`4ARf~sTb+3lRa@sM*RCe5EdiRVRb&)t@k~+0 zB!aNH9~`%K_TX!aN!gNZg1P6D6_FB%5_OhR(SyU6(5CYi@EeN*0s_~GH5+jC3z$Wy z{T#C>dMV)?{Y{vH8?Sy`G1lT>#baPQZ`ZDFi#(qrdn-?t5m+W}Nu*z<4!CJPW=iC! zZdN+&W&?k1)8AQHpeV|YhN&DfMy~uSE8d9ihwa>vHED@zx2#)08>ALy6Lw$(Uz0G> zAVK?6Hcg9(4wZ_q(@jT@ro}a`Hq`bzs=*vxB6CW>slbtg&M()`HvY;_rPU?UtmBN8 zwrg%E$nuO@RF#MYj|Zjf^~lopQqDP3U1M)7MKhcgrVr| 9>~PZj+}9VHj?2E(J^1bo<9Bj{IW|6 z$DWE-t1yo_pZhsq%YyYOh6rjq^B-98FO3 0{Xi%f<(d#a4+~-n^qrO;hJS+kbb5+ QytVfZOKJA-&Px6rhc|vu?R>BfntQ*8jOqQNiE~AjsSRl3 z82--e;+-`#%DQS#NgQ>qSgjhosMa+@98=}S?x_Z4t^exdIp|MNbXD>z+Bckg3e|jG zb7woiLZ?1avd{dWzEU*Ltr6JReZeu!C-Z}Fam@64Z9ll}M={SZZha?T5Q=v?4km33 zyX{%eopWQsRF6iiCIT>@iNXCL1Vc-Lq)dVH`#&0j1TfC41|Il8N9&}B;-=pQ#FjHa zZ2n#{ux#Zm(d5zeG=7Rch=+%X`dVRBd_eK|ns1iWkjtD(K_T5+@x+DHt1}YGuCtk- z*$7C8PZpk9ySCfgJRF}yX>Y9 fW=JaVY@E%{_RO*|AkS3e5>l>h zLIj3`bwFyR_=^!Y!WysM)zmeV#W}b0Cp>1CrN^h1nWi%^F-Hvdb6-(y;FjH(+Bk#3 zR?b&^vJ@=V@3}iz+Xh%mVq;=ssh9mjUnTCi$ylmb>nR1ZNTDpzB_V@!RF?`p7k|@^ zr52q@mL}uH^Y4ddGlD)U2{Zb<$aP@J-fC=qfTVj6GKn{09DvkzR9wVt6g8^ep%5bK zxqY)4iYT6V19c7sCzC>#MiW(34Se9N?SY9n-tO8a%zr>^XNZ8ht{gTDNv^f(l`NXh zt#ioJ3wdEs7r0ca@-8B&mYF-6 k z9m=Z2YD^QBjpDuCmwNiMl@n?-`VdeQ&ya}#W#zTuDHLx5>;RV-+41~Ra--)kyJGkJ zg&b4()^-1kv*{d-?(yw=?UNX%Ef8k LML28EU#WkWm7TYQmSbYFfqA z`+Ksi1*fxDS3^e@b7I#oiDvovGGcpQH^LnANc0Ne@S|p^Ts8}rCyg4f0I_wwYSwER z@zqIvJjTd@mN!la!&BsV*gZaT{@rkR{&Jyhc6!d#+xZI(!rAmj+IO4`L0Vx{T@HMU z{cpOWztRgVd_Op=1q^wC*xjMQb8#HDjzKLiB@Vr#rfEall3CM<;8_Eq9O@-e6YGR& z0iwT00qPW3quT?`7?TA)t+o5?8nIsK0U?U#7n!^O@7U=6ILjgyC_xmsOS|zTn1wdK z(frgux3}~bdo)bw-3_hGaD$T&k;JLjipMKv+_Nfy6Xv}aEIcncBHuckN;ppErDOX^ z{v~a)Q23+sq1H=9BN-oQGQG37#>$!Zm%)y4JY5p40lLiYS2?*XDUe7W3=mUb6(IdZ z>6die`jyH~E`wn2@TmM?$?+8X2QX8p;wL<_-B?8;`TXnb*!fZxHa?Nz;iIyN43TX` z5)NMf!t|{mgX5&2yH}z?cSHcnrA|#_=2kQ14lCoSO?%uFkyTC4NJQ$2(0;H?2Rj%b zoOOi+Oj=-PVyU%|V@%E;($BJOtiexdrQ SdytPuMiKWF#vv|AX9`i!L z2Y-BhX()GtQRiwZPb`dfY>Xkz5}CzSDRUsS+EnV|W}}p}|0 e8Q*sY z46P%orP)Xdbe1%A@HnoJ1Vu2~S6R6iE?L+l)TU6p)uw9DXcsLM$zvajZSCd)Ny~2p zIhnOf=bc+JIR>uS F>J&mUUI zM-r0KfX%7PtEQSuy43={4_+Ny#E1MzX&K6iu}QX#QsX`(L=&w6JLZlTu1pDHD_HLw z)Dx(c(ltWJq-fv=4i}q`*Ha;aD(U=rqP~VMu?fPzo!w5u@?bzCHdwM&?K{`oPv?|d z->BxNlVqadw!pLRCGTg`bz50-?0i%r%G{0z^l9=IDw4k+-Z=!iV$TeNn^Q~0Qm&TL zI~ilC?S|{0tI|TR&I?xMhz7sAGZ89mdq>oLO*U!%8r`;TW)<%tWSr2y9$?Kp=i_Gb zQS;|xd16|(n2b(L$(;sJomsa|3sFq)w?PSsihB|h7FoHEPDOWk2wyc}%^>bZR}$Y? zvu!I?JP57 qMUUjdGlfVvfK*#&|TH`XK(@g;@4?z4lHS z4Y&z5^Y Rt^twgCfPtSm{2{Ge^lo^!EcARo zM&cA=xjm%PWw|TK&DVz?a)D_MMvd-jS?ORoyavmnRGO(a;?Eo{+vg_X^dCBjA0}N$ zkACc18VD_YBNpFR$z&pVA*kXa8|GGMZV?83md7>awfj;DTa(AxWWeaL&VL(=%I!R^ z!14Veu&3&K$sHOS#Rdz!C=2Gk$GAydxX1#c>rIA?;@W?du~ar8Sf=b(A3`c@dXfJ< z5*HSvJ`2LC7Fd@oF=;e#O@lrw(VGEYP>9!f<`mZObLjU>%;g9~4(2RW`tO}EnG~EY z=+x3`c+~k%9raC va`i e-R{OX)=D*aCC6g$Q~jnRM!PeU`EzSPsvDQcD)|PRpQisFhFkFi% SRcXnN&YV%AE&D&(bI{JoUZ3d35`E z&`bj4cXy>zXzYyQOm9R?Z*odK)GNI#;oHZdn>+Y>uIl&@ 0hgB8Q#s7v z^twzW^*zzxF$H$c1u16mGAh%PsyMF ko&L{3Jp{bKEsXzg8lIl(0xLrtRM!RGJ^n|{g+ tDaP>4PWt<6Lqz7hO7k)Fr@*L_Z#q%KQ@@@>7)<*ZVnjUN)UbRlgcYyy&iYlp=<$ zoBCRgdgAAb%zi)=a})DE>r8SwTOtL{v=v`!YNMA7C*QQ`p7bwg?M`+Jr_(dFj`yR- zo`xlqHb!p96*cLJqP{O*`{7JYalLFD&iNL{0vAB2_Rtjv*J_uYLx)Z#F*#p7Amk^a ztAYRJBJ)o`vIF4w|3e}I0BZvOf2+U$2ZW36=Z)xMgu|N-{-py4Ab+91N*wfGQmsS) zUA{KQfA}|YR!UAw1j4=O4$wT8O{Qhj;pbd9s`?K$4x@{_t~S`s9-3DsAK|mO7OHc8 zz8IwGL+>XFtat5FOtxg??fuzoH~oe% 3yx)jh;uuH>E^k6cyNQaLqlqt^#(5bdCT7cLC&W8Pe3DxFeGXtVE@ zz{)E-r++bR)8UR`+tBB rb z=xIzWRCTgtpotyonb&g6Wjcw2zL`4;ZyIVue|Bl#8w_GXX0-^`0!@Kutcz0gu6l4L zBmApfephV^INS?YknH2(*=axf@)cS*eYLqys9)%_Q6GELX{KCOYME^rCPO_X|LIlX zzdJ3(Xq@eb4#{s~Jon%B2&^t7%l>@=fDj?~gX>@-2R+0rPZ(Fr$dU-;Cm+{gDMM5X zOUh(v9*xu=OvtCNzfC@xG&Tzmx!$3SG0AH-tWETmZqe)%{Pa8s(3H@24vLdd=0rT- zuxngRi#iu2EltGFh&u6j*(zf7ohoEn@H~GJyv)(fHpsmsL;dWfXtN*!B&uOJU%s5D zyoNdj7i%H_R*zU~afuaB@q$~P!{nC%t&%l=u~l0ATr@4EpfaiBjvz9+EYMlpe=k0I zHAh9dw*KyhzD1=htk>=DX%Wfd9C7|`f*RO3l*ANHn2<0$Wrb*zooJ`zzr}gEhAU#? zCm HiXn&uQ5)Hk!7m8%*UT! y5r(j9hKDcLdr1T^ zpc5jDG(#UGOJ-#Iv?mQl8w#mGdzz7{bV_Hj_>1+<6XlUPPTDp_57s^dy2&yC?;rHi zB!BqS?O{wW(iR>%i$oqflq`$# ^BcTYXPB9ki zI@?OiJ1?Vvvz;727Y(v-)tRfz{+j#pt3JZkY)*Kk&>~4ai+=6!g{PSAw;%o(pUPT0 zKG~> dFssQw?fG;Md_;)-kGmNsOVA6{YsdbIvSWfcBPj zE99f}-v~S+y*Ue1$fVmfEwi^7We&Bwad#*d zgMhy&$vLGh9DQn_%MdRbwJ@xt>w^m RNv^Zb9y;F!K|E(CGbiP&`~x|m9J*QkfX{8%8lMaS5wSU zKRc%Z2}RDe9d}pYIswzF_Z4?gl&X!HStN3dklm<6pXssCY)v)JMu!HfgCAeypo=q= zk1CuLyR$?T_&etD3j*z1t)ealmvAcf%5d!d>8_oBIV^sn?J?TxwJyyjiZkA4b0@cb zVM;MzJzo9=akdyM{+3&EvVNrII)b;=GsZi~ZPW2W{57x6z6tXc3OB9Kg#-*+hhm`o z3wsuigSV1ZihNr4@pAQ?NKw~jr)PHbYma8J7tisKG0Ndw^*n^G-d$lhs)dvfr&R;e z(*iOP%T*?gy8?En5~bFsPWz&w#(v7|S)g-EfYtQ$7;i)DgvRVtOUDhgEL2b+2FYbK zmD1$TJyX^6%L>*>ChL2ZoB-4&Fj4s VxLNzD|!d( y5DE3H1;uie)>>GL{vp zQazWFeVmH_rHK@W^h9$Flp|F*g2Z9M?Xt)-Hw?^*qx_RJRz0T_QRFx<{kx+IEEk;= zkAzIypDd$g2Z_p_ FjZlNdgHJ2{<- zNlTaTl?9ZosJ6qA_6--WCoCqwnl92{4A~7f;}C07Qvg~I^tMo+N}! EiaKG_pTjMzYbUA#2zK1NM1;V@I#m{{{TNng5wAtZikJE H5J_o2-;HfQdWINcTXIjvIhMWC zJ~QLIswIUO5bLM(VZv)!jrqzZp6wHgGTU{*IfmYd-3kRRBE$RUb8=`YrO+gqJC0Op zQ0ph+>r|pjcgOiWkDM%@5Ro@B-7Nz7x$?Df#Q~G&QZP(You%mdocBP+^nHc8!p~Jj z3`+>GRitN$F^X{@N7$B%1G<%9Fpe*+_ )I?DlNO*^-FD&Ic}0CfD4{KWbghZIZu@s@tU zvf{1M{3*0=+K%(Kq6!_?>+=^Tp_QQFBl=-?J>xAmY<5v;WFA0-Vp;-?PB3NELbT8w z7p#vkY^=Vfq}WA*?kvT`xlwsy^t%J8knMA8-TFL-o=*L3V*ancF9m4k^6AU5Pedfh z?~su%UrZRQ%_3`{RF=Hj+AgY9gc6b+(yHGvxsGS_q20ZT#MP;nY>k&=`s(?Lz8)Eu zJHD?NSLqRmto_=SZ`L~8O>f>{#kLrK)&X5CA5}G$o$X3uWgEHi&s!xLf>V{8l)g#f zcB|nU8YPzE*9?r@*Q2vMej$m2k8F`BjRp*7+^HEkdSdJ EGd9 w8k9)iT z1=rqlk8ay E{mDj;^9Vt#N+ojL^&5om$VkU2mA8zh9Gg4T6%@3 zaV{>S!%U`~U#dl46*!8bD!>g>8OZQ~Y=!NhmzwzQx&H5h`G}}KJ?47cb3TS$=16&7 z&sm#`*XF-G n`Jv5&xY0+zA!zOM3sMr3!R4pK&=yGri{G4&P((X} V0#Qfo!*$QtR$6eL^)Zg4l}e=Se^G&(lJhS0NB5G9HOIcj9zHRQ#i8Y{2qB zuY1!Y1FXXZi&k_8TvdTupNxN#P )qP4cY rxHP&23L0&3e)y!jE0!tyeXQoogH_7x5GAXyFRF&dEgy(y?t;B4sD0{6H| 9i0bEv=9&r zf=zf^YkvR@zoO#jnk|zmFe&@2wlO`BbJ<~`@M-1UnON(0Vzzv!*}Ux3P>EGt>D}_7 zMT6b^$58S`$p;tK#~y?}aEl82xAOd*e00tS+?;s?KSU?Du3_STDWx&u0M^xErTQo= z^s~k-{IRrk&jm7bU^1CN1Ut*{fo?H6m@Bx$&8iYre~M3jSUUPn=Ga^2u)=fot6|ai z0#(bK9-^>wo@eOXv~iQ@UyYbl0V}T$4!s*M7n)61fAAK_eSDI^MM~i?sz$_77RV42 z&N^I6b=NR+-}~!-2({Q_BOQz!wu5Te>D2FcwgwDS#iOE2tnlm>&&Sl;L{5{|bu1iw zcMoOTKB4Dh(@n44toT$~wymkt=+MFwETbXVAjV_52VGJTcAxRjGEF9rC_RWUb1YNm z4dY+hd1Ec2s + z5`L1at1{QjHcO1}z|?3E3sm)w3M!^wS@SKf$}k-gTj&MSk^6Ut?!1n!lGT1(VE##k z8QGIYjz3XI%IXf+#p+w(o6}&gC;kR}cHUVvJ>c~k^LMd^O7-vd;c4jT5kpCcc5SbF zO1wZTzv_Hw*hjEWL$U%M)2Pg#!W^(wIH6~Q^-S-|&IMkFLU@?G;ueKgd7Q0FL~7^W z&J3(*=H=$DlTCS=?Ks423%Pr&_v_;#f|Il|qsX=M*`|1;yO)5i`T=Ix`7|CH{a_-l zBV3qq;0mGg^%h6S#mCH+3fo9Bxo0TvPcqQ=5yp1I+69d%q{i)(gNgzB<_`tfkPoi( zNQ{f$@bDx^t1l#mgJMO&A%ogRevRhm%c!>^$)e&X+X+mxqOZ<2LIyO2CrgYq2J?cn zPY6{Xwpj`ch8)kCIW)H1YDx5goVW1Jo Sz )w2d;78xLUY z2 B|#Qjz5t&+nD4W55J5mzdT<0yS_>c4mPbbQ$j!r_j_voStq?_SBW3Ri^th zIVaDwic%D2BV@Fs7}!tWn;aHv2!4SGYi)hHbVoQ`6DMXu{i3A(uEX|IbKyN3LbENV z+T|(6BA77_un&@WyIScXS8p)~UHz?x=-?`KmX#Q>8A+SUN;;F#>E91j{=$TTK4qbN zmwosp&usu+{}TJTTu~Gq1RaQV=e{?Ok8
1`xT z3Wjl!YtLaJckcR1;|~(hbY_}%5mgQ=`Soy@Ws #V-S|$I&@i6UZU@&KQV@pN_U7-?Vhu*#BU>NpWDo^I##@o-fwU{@d z?&nBPb6U+(OoFjDtO=@Ot&tbXxj660YQjyST0xkJ#12Vdg@npp)9)H3*fGX>URG7q z^HI6yE^d#O_%~OmVrWx5uAHGGUFzRxm4#nh4?)K3ORM$|U=G#zd>uVBpyEZvpYy6< zgQy4N?7VNJ*fvFP_ PCXC>IgNAe=S41$)zj?vfXR&+R!+A*W3)mf}t%(F?l zWv3e^RTRAa!W~I(0n!B{Nl<&sj`hEi7&hFbX2g2#UkVgoiZE+i4THuJ pExw;u}Z%SbtN{P=O)C^_1Va9#=&4NU$@|%g>3YH zdI1_~{X6F&SBuf;nQW@!T2V;pXQylAgQ~$2N6saxnVt{3N!y8@B?kpKHc|c)gZ@I~ zMt%h>Ml#yTTG4H+^_?b`5veqh=EP};)|4i+(J!b-F%)*NsW7e%{+qt*oN-dBkVq`# zUQ(Jl<<|F#P3py-6{X&lKl-bFFP9z^zcPN`qJbMMdl`P`UxmfA2)jTaYZI7KFX5^p z$AnFRp?}$jJ*(bnGirlX6=1FuoY$3497ouy(r{>=+1s>eLNfc?VA(kPsJx2k=5+Et zPsshMPHpO=qnHj+9cfp>Tb+S; 4E%$m~?EMvFdNO_jPyYGSvO{N1laag&oZrkqpI zQ2@;Mb3kxvk|2nvX(Uml5{|Cmotaa~iib#Q^nwVAe!l(yc~|60o;?tXhn|UZwvMPA z`IX3L2e&tFUK3c>TbC|~an)6FW=l!3X>+G2UET4upOIr-k?|^IN jkZcV6%Aa00 mC+Hcrk?xTOl)b`yb(ln>GkYb1f+~E3h=lZVB z@*Ut&ly-!dV+99%K*DbV L^ tgvieB_e@y_1z9V&>+&dSO{M8nYm zPH}tc`*E9RQj%8clcA29WET=zXg~=43y2z&)FbyncB(tcQFlC#V7{?Us?_VkO zc8|TafNVFPM_*8z5iRDuc}T@rv9h|5+7K7FWhk{D+t%VPXwZJ8kO_aQ?yx)Jzu$~S z)Du7q_j$ee`1--BSN^wQ{Km7xv!4L+E&{xE1bNa^qryPKr`c@(MR2_RopwRhj!YM? zrjZ|x=L4Bu+(*XT>KjxvESg8ihmSBdf%%A$Azfcy86Q|;+EidsbpJ^e+0G4_F-Co< zyLL&6!irSVMNITd0RskiXBYkG57cm0cZ;PTilwu|NEd#ghA)8sgqdhN!%3y(zk*&Y z{FiTs8sXO=`)KycUCGGLu&^%hl3I|`xjUDcLaWkNg7!p)Nx{ArwNy2xsH?OLFBai^ z@EI7xkR6z}a5ofIin*ZI8mY%MxQ}Ay)Uw}0+9K|R%DT?pV!Rr&>p?kXIVnqot`pGQ zS%@u2*xQfmE? z1p_k6 zpS~D51XlCQ4a#^Xh!7f9R8%0gH>j(tBjvGKlfeOyBZRqoY!wz(8Q8`c03@vc6FT16 zekv42;NCSt#J>OlbUOb@Kl=Y12=D#`{5`#PZbVQrOGC;Uznbnd?NzL09uNZG{{+R* z{9o9!ENBbqiwORb)lkLZbe`~0$A`6XZ5nB_{U=6dziG$Sb_pzG^9i_5k07EDhu@m2 z>MZ*vH>q=+?lZmko@KAxd6Q5(UMq(ZCVjs3fBUW2N0t&3W%FpbW-K{}asK%rz+1B% z!%y-O(%qs$F~`m4yfhz8J#3WH?6x;#ww#KP)v4wuAacKtKaD-fH4%$WZu3jO`&l$z zelEu!(N|9<#(IAjvaNA8JU(Wy$N@Sj`FsdP+rLh}CZ4rPC+O?>dGFRTrAkk-j m= z$Qm|Ertq&be}eE}`O_e;A$S78y6UWQwiAM-#9Y+&e332VakPJ83;=K4+=&6Vr5H8E z@Gl5RcQlHCFoor{-&s%Ilf?uhwE6!WYfKL~e$-P1xBRAlaqc5e{X0h`!GHHk{(crU z@jECkCLfnE+yA%WuXQqpSnruOs=D=~6$kRnlO-p1)?1&9`CsQd#S{52B5+Jn2iyNY z(VGDOe`K>y@A}^sO8GzWe*YTte=mVW#6z7-@$c?R-Ym{KReft~U64XD)1wcM$o(n4 z*UItH9Ys>|7o}F)ic9I0hb8Qa@cMBE@j%(CtnIC~>8j89omPz5T|Xi+m&ERs`R$CB zcFzI=R6FC+`Tt3hYQ{f5iUaN!!&Z3>vIAeElQNmzazMSpZR%9Z$Jo9HE{S2%PKvBb zE*DqsEn0#!#cqBC3v46l>t|)%->R;eH`(XZ#z~S}v#`uAE-s>By=-GS2ktBKey~C5 z?C4-vmyJw){WNj%3ff)27Oke#5G-fr8Hw>@(g=^L=G%M={#Of;+0?s_u(z7L@TG84 zobY#bbN;Y;p)Yt|XxNsks5i_KpT_g<^20O{UvKOtUT8m+x!)u-o2bjuhJmO@~g zR#G5IsjQ${9_B+_w`iKO+XQziPWOV8mLACu|Hu-j^7xG<#Az^REsiGb<$Nu8`*u_H zX^7|l46#1_pCJl=2h1^Hfp(LuopKUt++6~mpTO!Sd1m`T>ih@t!#!uevr$(ju#${@ z^#y$Uj>b(bSm70vv=L#X-+b-tWE& %l+uDjAKlP4kLMwlum$O?G@9 zUn$KgD7hT^;g|^n(of$53mRI%cP3$xf4&DVOeDKm>%VPY{gBq&hZil6&)v%rGk$tH zrjs@@LHhAmq{FSkWc?-=88MH?_|vdMY?c1mSvW%+uq5e>&B*`Bdo+SkI5~j_ytq)N zV|```>a9x6P5p4?Z1TW&ebRU*zxP8a?2XG}9E4@I z`*4i>jueownv&7>r}<8L%Vn=tKikGbPrxaiq)f%s+ACxw qpUz-psxKHjPGqN^ERRa|0sM>2MVx+>v41Y>aJJ zPmg)*csMgst@`!r)%AEkCIuz&gNzey3+$Nz@uutFqQ6;qPSr)2#N0>kP9k4ApVuh6 zRn=1rO->KK7D<1-pJ4K-?c`=M)$wAS8N@)&kf3>S$qwI)MO*-27dva}nR u+p%jsa4Dc?7mO%Zc!kK5SBZgb} zla|gO`oGXAk@oBkaX~)p?8GLyDD%;MyiwX5kUkHiRKfs$k=IR@Pu(f5>f8^rx@Y*} zo#=#ji58fF>J?}8yz$zr;F*E_udoyc7~Ma&xTx!?Ccp~aLu0CuIo;UfprCp;uBAG3 z`; S9a zB?#^r)Hol^q|5EbPluxwx|=bvC-b@zG9rh`Als~WVj@ktVj!cfL7i9JVaL#t!Fmq; zUHCDBttVTTP^2x+jH? 0Qu<2F?CDwQyS)Mg!eC7m~ z)YT?X`NB5-_<8?Z@$Y_+8275k@=A4$`MX@Py8aTO^T3c8?1DT3qKj8=UE*pu_&%J| z4`Et6X}x1}^D_F}-G1Qs!O m_&PGpDuE)!aPGMWD *fwK0%0Hu9c2rV_+kZKZ%@G3h8p zDS=pj*ZzIK`(XM>4-aV(Lh`LNypln3fiLZ-<$9{zO^VMd+}3^4^3l~peHnaKs@(YA z9oDFC6@dvg`s6(0-lT!DBC~{?2fCV?RFdiZM CAmA z{rJ4uVc<}aTs6tbYkMb^c_3rsrI@h$Zno?1*_yh#A3xk3boO#M>%S#5)Phu7-v@ni zfu%VAW)ajaYJTT@4*`|WsA!}${Kmng90Q-IK@=k+!(Jbk>vA%=Uz))I-Mzi6N`o4@ z>1^InhvHkIqgmu1ANb%M16(E27%9=!8vUsvp`*i*tLum7(_Vbn&|ec_XBpscYZ`BN z@1x!+;Y4JKb*qW0$E#|oX<3yikIOJ=rdff l$rjL8*jXy3%5dQJ3I29@WyhT#8 zuzF>#bCJEeXNQM0+nAw=DX6bZqi;@8@Xl;Ui#-DWKfQpuN6$*{`50B}{5>9rUMH8c@w>K{jCmnSggn{)s%Vz0xHw(m)(e`+f@WFwC z!Y286FAA}hm!Pn5`8Lr3Da*x35^Vl^Mp^MgQfkZZb?t?ls~RspKIsb;yo(_Vik+|# zt(d4J&G;l``fOeEk*cN6u*1WyX^Z3 SIlQBd0-v$YY_UXM{`t=W-QITh2C v3q>*b`I&{QmH|AkwO;^E1T$3MsKJCdDLOjNMEVM(Oma0}@KQTW?zMr?k@&21Gpd z17A 9T$npmm7TwJcNSia2NAC+o?ed?j;MiBz$`Iv%hXzv;dG*y z=vc?0)T56X-uZFx>ljZB_4o0nMTBux C{#*Q)eZ-q=lx;Yn_GZtN+&JP9Cy-LBX1h{th z4>=!QzM-)sX^hNHYc{8G!>4)+%u>(6;hxi<6Y;RJ=&&y{F+R$@wH?p%m9%O68fKYy zvI@H;pya_R*&@h$?jV)5`=cpP2mdIN^M$HpMpr>PrS|26h% k_59%}VN5`caEGBti}`DKPV}!NtX)9>V>h`N~t_Mm(GR-3o!bPxon6s%K#+ zvlzQL({z R!N0CBlf#q@&}u5V9al<4X{ zLjas~L7{k;G=lOxR_I2Q^wngoyd6z=8imp=Q#U2Es$~j0qfa7>?d-}N-`DjD#gjo? zJ7dGrGJQw18mUn)CftA^ KzMVu(lW;p$ytvZd>;C->=CAQ!f$CuX z2L?vmlj5H%e>=YJ)dXK`FTF7$i35`RaW^(=$^SA8+Km2_tvi%=$kj`F^KgG#yWB8d zv|ZwbZ-7g)8B3FLJbv|ihU8>mHCC!&LYhTZ-SC)l(C~XzYNy3H>g|`q9Aa8?{aA<3 zvD9BbGMmEta7uXs-L!c>q&mCxB5Q+d8S8${RJ>)PEBHdPDEHC_Ursi3X2>1l%%mTO z;vx{0MbN F2lB2H^GC zmXBX2VC#7`yiaODt8PQUB5m6YVU3iR9@hMl(dNwLn;hlr#zeKIO)yN}+vZi>9kj49 zoc||1lIXW0XiAE%nCV!8a;*@ZfBADdr-*+)FI4SLbYVt$1W2Ng{G;^@Iy1ujXE9S4 zRc13@BX{|g(u8s>;a-W^C@Q0Hz0k>phNaKJR5mBGm8j*x)z~+R)yagd;K1fX8v#D? zj~mmgZln~C8&jnhsmDdR`M^@}tn_keGk2spz8MZOoNzN`7qbx ? +Vn(7TywP2(Ru=EM-r zn4N)wwp1ixSDTreu8SSIpFRMUPwYYp>O9T-BJZLvbsG@3wSc`WVwM4aK}t(G^cZVq zXY@K}m#fVtd$u&~r3=1O@txUI%_@XhteU;li%FGUt*Q)!pV&{J0+SYUy}~WBO_smq zW-pqYPs|wM(O?@l-lnFPc7tCYzN}(5%IwH``>o36#`xy&Fv5MUWPeTuBnZl&2VE8H zacqX4Rv- Kxc{Uu`eOI!|m=KZQv;NFHkH=nS8X!WYdCq~)rEwa7i$Zcn|Urog3} z_nVolldN
^uOM=ePy2UO6D{gZ8QLH(k&iTtEMB-WuSWMSsI;N+ z<*@dAo0#EOjPUd|>YohgYIR CJFIjqwA*V0jj+TpX8csxLz)!R;GSGXd!OKiYfmu%?=5Z#0OA zN>folK@d<;s&u7AsVV{j(xe1Idat2`1Vun<6a ji%yKQFl)p-y{+rCM*=Pk!wzjQ;&+VrsH*y#tu z8;)SVdqPgjE`WjU@t1*J_czc{O?hUf@PuQ)di2(+s`AwHFvefL6-ra%KD8UGD5h1$ zLL-MNcx{B=y`39?MPIu-GxNo{rQ&akzW0wszqzDMzO>3ZiaW%f*@^k_S;4j!k|p H+~Y;qq+REP7Cq*mhu@y2) rw3 V9hS zLg%uc2XIP!msya3<&qQtX$(#n<&&3=2@ lwv zt^h<3C+m~-ER{v%6T>0_y!qXc0ALFI(!lxdE9?@tWcbDSVUMlJq^n-zj+537Ra5=r z!~H(BwY7eI^;>v+$45ZKTWo(s-23&Hh>y?vQ+6C#9uwJO60=@Y4?!~R9IpC1UdVhy zd4F8Fv~8ePSZq=VS}-y({`zo31Y_>#Pz#@LRl6ty1iPYP)T9oRb$s(K7!p+6^4TT5 zje%bMf}YRnUslay(7&K|Pbhl-B^)|v`Om5SzkdI1I2;hGToZ^Np0&gW%sn(P0M69= zQ+x-e(Ut4M_r%IDI8_u1WsLUR2 cbN{IvUo; z6sahJ6uR1|YWRw{*~ehaiXOnJ@*(hgApef{^=k?03LL6)fgg4XmpyaI9_d2 zd9r`yqHBeGl|ohNLfs_A;}pGr;AHZdnIL6q)IcAn@$`t}+-ziV=!@Vcm)#|`RrP#N zQ7*2=5?j#+45@jwjUyW&CI=qcLc~_TydpU4>RYGOTTsa_XwYxNj+>x8Z ^pHzrJs)lFn3knLbr*=J)l`a7ezjg}hW&8^xfg?E@d(}k=KC_Z|LqEcN z`Ym5dsw(vNNve-iT6hp>au))Eo%$G>mcKOf9eTr&ur^Wzg{ZPKmUHU*K4&_)^pU!6 z=y%h|#`h`0zm-Vn`z U34ndSoZjHMbvoVUZqG4Z_@DchPM5amrK*S*J@VfDa0-Pq@i&J+gC2JiI{eEUr|U zD|-ej?mh{U5(vD}oxC`AXs73}Z|pd_m1%Jawk|~MPik2*hv1X_pn}Q5<}SmuM{4Dh z?9W4Y?cG2#F@ WSDa%K+OU#j4jEQbixwOtb{0JPp0cQawEfYhN6!U@1!2E;VkYpNE^(67muRa^ z@Q0b=d~2Vj_o7EFqVIOQMn6!lqVyMh3|haekqnXClej#6LmW21Cj5f5$o`1`ef*lx z7UIiwfM&D%%y^8=okJrMd~!p;F>H+ahD(CT11V|ly&+bvcVDiKpt>m@TX`HoCra4b zWTP%?YgfLTJ-Qb*%yI7*XhEr}w6W8SHpb}_aZ!Inf*NFcB~>jo?|@8LKxu|BsAnpq zyh? ROlJiV9_YIMTMV<7r?DoPb&jk4S$f<0c=ki@^r{ *0gS)jJ@tAq1pu+di>ckCzip8J3_v`5@ee1lHoCGpQr5~0!l! ~G{RazpLu#|K6VRJuUgZ&0ACSusQJPX+>=N~{SJ8l-X<~pz z4Y9S6v9pWB$XagvV(1bM?r44IA=h$aiAr8yBC~-Rjal%d{vd*`(#3X9Pue^hnxS ztzILT`$?Z>gB!_jduDAXAo85zY&>%J{lWRFRbK%1^`^KEf=Ib9jsFNy5Y$^QBK7Q1 zV?=dch)r&}PID6ag&faZp-w(pgJ`Vk&bNUgpW2Se2t+DJ4KGHk)e6kr>fPF@49@oD z#?KTf^J3+_Q~D;Ns(J2JyZ_iB3e49vG5DWGYFdppl&v{N2Wc!)>z<{b4>FQV4`ct5 zIVKI)s`a#JF;rSvs&{bwcqI2BbgIZ}U(fT%Pod^69z W*mtPfz!tv1PoXwDFZyHavT3ok#AXbYwgQO#8aG(Y%AJ|-rxw7k^g zMwBLWG%3xwyms?Nw!h9EDXVQ5{uU=RkOEsj>;UT&4Pr~j_1Nvm`0>h5l~x+eno?xl zA=mUBLq!{i!Jhr(;luummW1&=<|`xR2}@GLJv|eZ=GjQU1^1=x6ZakNMQDO`5GnZN ztM;S1pH2)#4OccJ6NLqADODjYM R}yx|P_S`2q{H|!SVC>QftFth3_BR(I;!s8yc6yzb9c(Lf}fLfoiukyGe3sm zR3$0i#(dGA>~-hXb *Rj*&p9zvSf@JfGtvBm<6TwPG?xu6R1zcVN zbeI(mIdS-}#SVqIFL!beQm%i!mo#A?8 E=Y-Ct}=Kg9#wVpU@K8WtFPdmrM^}xhN5r=BHnQD_4#!Gaz6Zi6gGExv3Qeo zKMD7JT;PzPP8^MFX6|3?y^C*zvPcRg0h>CMo$$-vBv>K5LZGAg`&vg`fX8b_8;& v6$RX&3C+ sxTwA#s^O`IE#VC>@C4UVK?Pnh2`yv8@e z6r!55W4tVQZAoJ!!YEdnhJX1^Ci(%|y#M$M1;~qgOQc?=LKI-E?dyRb%EAYEAnbsG zsL>8pvDs-6B5L(ZMxEYMrlr+Beru9j`+_n(Ct)ZiB&h3lJjT*M!fB`in*60$pE$?L zo+X?v4e!o7&@;09CW@A5kxRNcaM74ut@C}-f=Ud>1iOubU}K57=M-BQd-{C!+4rl( zJeh{E56!s?WBA%J@knOJPfhmwEfxkQwFa)>BO%s|qcW#BJ-FhFwb(V!-wXy1mF5)= z?UK)0Lf!4a-1m9DOR&UQ674rs_>^|$u~r%!hN|mu+$a#Hg%iwwj6db`Hhf49d?q6o z)8RULj|n;M$QevjCj@30D$~dHzsX2K^7r_C!klCRRKe3H9y Tg`3AQZgIaQj4+aKgDoicJpW2F^p|qx2j5v>)elz47XAz4L*0ImHqHx zqyo)k`qTJl>V`H2s|I-Y?$13-2AJ9T?29^J74J) zSzPx(B~|7wpQ^EKe_R$rYcCd$++DG>8Ss+(hJAEYFqRA>7nYSGS)MY-D^=AlRNlSg z?@eQ!yj=ZYTN?V1w)jc37Vb`gqHSl92W6J(5Gtl3JkfUQc36HwhLXXD7cmIgvD{W> zwQDtt_fMKI;*V>@m+$T;$sWNI!-`EawkoiPF)Ru*tCc5=xKOmKr=4Nh0+q>G{gs#U zbW;qUhyi+ `;_@-Cx4`A*)E+`e#2;0F>-4+ UhvK1;O;qtXy`@u2q+d1m$ oT|hTh_T*oh6$GueS`apX3RQ|P6V9F z7)n`CP&SJA{ejy>1uf$=c^VC*i%5~;KweDVlm70+y y)r*S8?(+L~P2ird=^Pg7d`u6jojAE`Pe@EXyH zx`^jvYG 1MOKu6~FIVy6+I?|FWDKqx?uU(z(+ z?K_r!J(c{syQ`J<0UeTEK8F8{BmMZH-rFl*MzW+R_i@)P_V_OAmL-FyZ#;52cCPO8 zDVfXIZhhwHwn|OZT@IWVs(Fm8CB%NM#|L_Ag-cTFkb1L943Fj$T9G)>k4B61#*|`F zap|etSexrjrtC&G^L^G1JE5^3(K={&8IyUK0S2WdK2kEOqMZEpvhxQ84l?hvpVrI= zCtSBby?;B84657b>CaLxoPxM`OhU65MtU8M3OItgqZw*}Bci?mNz)-3<6~jZ4t784 ztTt!A+Irg5ywY!*FBQ;IzhZ~ss}tJSW7BxDt^N2!d66hr#I!;htRwuCZ=J2oR~IUu z>^PbBu^+HHn*pW-GA|785^hbM$cvR(WXGL9Y&LDLelpt347U%N46Tfx>sKlWFS}Rk z;j{SMchYLrU>%y@^NiQNXI*RhQ{;OkV@B)WTZZK16!;5i&8@?h_hcJ3U*qdC!k>=s zfk5t
Z_qX%q M?-v=+qCz71=+u%DDEIj txH}gl z0Ll17xMuwPQ6pNAH#wL0pmlC4y|?{`6l)Y~DdL=3^T@Yx?_v^kwv)wD^RPB*9dW>! zTpXvc)78t_Czhwf?Y&huv#60lnj#+og>kn2y~=gd{U-H7AG+x2_vR+|MMHRC-Xk%0 zlxlLdXanvb=I79hNwymu-n`1ZOuq$-r@6dM-LMAh8cKEsEvkuHI7OxgXJFp*M(RYL zM44Bf`HjA|%D^iGt)6PZ{$QliP#Z;L&`oaOl1D)x3T_Zbz-^?F!pV{O;|;~&=2vNMcn@RVmdyj^iB8sN6lG;0p*W^>~kYxM3 z)b;+aTI>%2g*P7wWWl(L3!Zi!|L#_;>2q+nJ%4=mX9^?8mPr@yEA2NIh)KR4^<(WY zb#ETCAV`_-JaqRJkRD5b<82(hv7>{-kkC9iBffBGw-{Wj+RHR1U}Zj5qL>%kH%`0h z w%f+tlvAEr)hZn(+<|<>-ZEOUdpcf4kh}uOe&kNCEn@AM~XPL zU!P%qaeZdnO~G2_r>+$g(7+2zu5+MLu?9nA?bcYd>#E=4sS-Q{9&-`UjY0hU39y~l z`uf_7->p5qx*AiS<9O@u!D_X=|BD7^Gx+1zu3k&;xlOhQ52mKuXKWuWFUn3&BP3lM zey7aIn>>^QG+DoaoZ6{KSnL+sW8-(yhLYX<-lknUpC (T1Yi1h&*`_MHaa zZ%psrEd7w$=?%&fh)<)C87ZQep!I-w1y`-*w+<(LxtT+U5a)PrROU&4y(l8oe%qHz zJRbk74Z?!a%E%dWN_K&H+{}^qeEOoKvx`A#F-p~z<}Hb`%Pv`7UjA^6-P&M4vmtt| z9dIhq)PufyiCQPqN0Bc<3&eQ0rL~Vf_PHiQD~Gk>ZJ5Z+l9$#6XP>yJ@V&Q?rn-~& zF4aK3Qr!OIccrouTIU0XjP4ND^SU-z(rt#uI=gJJK`)QwMDFlwWXVKRj0 l#7$))CNy3_4(E=N)I z+do)9<*{m&W$AFsv(32QCmVb%3h)?k^MJ>KW=sJ*cu^=G?4j43xVjk$K@@vmd}ZJN zus)F(8@oJ2hvy%+HOzmW|L$}>Yg*_uBWmZm0o}&7n%g}7z{%|f!$tNv2#{Z(p4NX6 zuxAbYa`wYNE-Xk>9vNJ|c9NH5Hjx$0WyRkHTQ_ 2c*4l)$qp zS`QG~nO^C )Cnb)SPl_%+I%})v zJ3j{W>+$@-;K2KX3^wu&m3l7hE&ZztK#K88Z~qM)eDEcMm7I>TbOXB@MNX5S9RMCX zIq@I>DEYe7Ak36 x9DExP@=>@7P!#}X8i6$PD+WSw~*IbTw)ba|a@w^AS zOZ8^~6qCdMJ?#wQ9u#!x@Dj-PEO_||)ql8pTSg|4_^UU8k`Vy>{6H5){K?+|tat{% zVr5JpTLbAaFUr^#jn-AAWFyF=fOB+Qm`Wh$!q`$`HuVyPyT{&^hwl%lA9j!i<1)RP z$H750rh4xe_Tr-8 aIX(Rux9af+nx_@oQgv>!cv{=ox6Dat6nNl{<`?2-%V%8GCXFUd(g}1~#^96TV z+W>C8VGv!&`bbegwf`UfU_y^c849I&!1B;JwcFC1wFD;FN1u9<9$bW0s9jsjGC7Pm z(S@8I-uq=D{{*jl4|)$8I~gYpTHa;r)}x;4C-6QX%bp84)R>L9+jdPi!0XfpdV1Hh z1Mmdo3yStj0FD4gW|sYcss8>(5&{5YH`|V_z|*Hxo~UJK{$T(A9Tz}fh4&H5ZOpm) zza1DL;~?BWhrHcs|HShB^E(SH`G5WSfBA&>zj3boKX`P}nVC `#h6%wzLc0)IbZ3t$X8 znmE~^T~Vmoq;r4eiG2d T1rpc{})*x=YbhVx53OLRo~g#yE6{E@=yPt zeORXdhcg1KJgNTw8X4+;o1y*RRq@jEx<0_$MmQz_uT)-9!T7h}{z4x iT_wbs7B;d__(?0yJ;zKmFhR(mp z1TAq(Osrqx)4$ZJR*9bPAe1MVwAP#4u{SKLAZ(6s{1*DV)IIO%L@NAr+2>EzTquR} z8sQzKUmrc|-=+1ku_Ol@J8mH_@$ zWXU;kN|Ddb??|kK3Ggk#J7-%UD+0Dz(4eS2>RSuO+&(*J!AgGLw zZ4}D<4__{6n~2!Utw~l#xb(*I8(f)FoS?HvNd&8Zl GS&vkmgXOw3*>6Sng*j z5!`3jRtE0 P0h2i z*Ru ;2Q~cx~|WY|u=iq+6laXVt_uowMh%q}VXmAdkufUW-5n%nvxiXoN{0e@*8Lk}Tfr zL{n@>RMrQ8ivx!2^c+QoWPe~d*V~kdo<7z6d&u#L6xj48<_9r5>693v!Rg0%T-xj` ze9PE?@A&(E^jIciM~?3Eu@J$+qP4-Vb=K{N%vYeWz0W$e(MB_692dRcPw>6#Oc9t5 z9vNvr-q5B5?D}+nI{!?*LRZ8*7iCu_qM5fK=2iX`@q3zY{RgeWem$v+n1M1d6&EE7 zQod;Yp?|b)KVBSDCgE7<
=+Ki$v`6z*|)@lJL_X zM4X1r+opX(Y3go{Aah8>VWj@|JX4w<5pLDa^4~=88$Eox0~dJP_SWMYcF}VKB>_JJ zmTuHhZ&U)17|8Pl)=V4l88}^TC(1_cIcCK+U5d} zv1>Y$sP+^9J2zKVG_~u{Ad=Uy8Xt{El6bcA6kXZc)LVmS*#j@!vHWB^8~CQonfcu_ zAI(?41Gg++YKA>&dU1~Kyyg`HmM104V)8WR*W_tL5iFv#X^KwaFJEdVycPa|>hotZ z`J%+YE7I)HKT~FI=yLnfsvAD#0PnYn)gznK@)C|<4|Ioo53s&d=!#tY0jcLvMXams zwV5}Da;{c6zVBUaEtj;sDQ>~hWhYja;JiD%Y4mItYX}dI1A5ZDZ<8!Y7C){8cd=2r zvAfSRSIjX_r-Y)WA#fJyr_^8}-5usJOc&;K8`HiY*FL_o9ni^xyqV`YzLU^RCEZdP z>1(%kx|Y{AKKJw {rk5=uu z^2gD!tyP(1%@O!$(S zt7WrdJSm 6;kFGeFvqc4jX34HHvRj9~K(q-Bu_O^u;=0E-NmqK>tx8l?iz;Nn+e<6HSa=raqad?!f0{c(M|R#aZ3DxV`uU3< zqFr0p?OVO}Z_sa1 <@GP5?Xk8K<_1o k_;4Ot%{fAQNV49Zj=%2+ORs5c=~2a9AdnCokM(V{bvl` <($F(g9X%%AYSZy>L *Cc1wGG !b!Z#-q+)s!rMzV| z=(od?b5g>zSH>SbuZSw9w#rlTv+sM>Mi~2Mf2ZtTVkVO?%1SDUCFj`cU5^mO9>YpJ zY<|i<88d8)HEWXO8|%XBQHIGo;xHUKNOFzbJ!rX4*)UbLoa-UxG)=hH?a}4E=`Yvj z=P(b0b;W;FR`T^1fkVhbd2PO8=MH-eqX|zYo6u*2Da!{^9!dMH^sh{vom?0GRCHf4 z()ntc=bMP}O|{aAM$L&6&jeh5%h!Xkv7NV7M9uKhDmu(P4<{E_r{g#3b)Sb;cl<7R zSyt`ZWoVW=-VX5ljd1r9K*LtL?FSoiyLiK0;Zr#lC>iHU>rC=Eh 9N-G(Gc$vM- z6-&M~;o)Qi_}v?FHyeJI_+F#$>}2=WARM>5$;v_1Uz%Sy)E^7 P O#qy%II#{@z zr-*6XPsu6o*mZCp4h0n+Bf*br5m(U|+qqsBi44Ru_~yI(uWK-U@Kul((?%P}NU?)k zSX52`{kUz{f9rI7{B`9PJVOfEzDesMGQ}vYenks>A^v*rk%5dPf5LTXi8lJK?kcHH zDPvrkH@lBhppzNB+{m}wOqcD`W5Qa}K>65O+mybqowJ2>%DZZ~H+cG`=3`b>{$q#z z&D}Q@;N#MrRt&I+mm0SGu0q^&+BH0g^JZSYge*FhI02I-)g1`9QsJ?Ey>MUZE6~vT zK-8hbW_NU}LtjlRYW$T$yLP8Va5r}gT!@{Hr#Ei1n03{oEz>=V5~^CTvmqF4q=U z0y+)kyei$eOqlM5C71+jPn1;0J9)?`h?;knbe=ND=gyeap;122_7Xk?7YECM#r~Ze zz2{UT +Z+90z>tbQ$)kA$#_o%0e5+kuxa}Ag$KOAT$ z3pg}b)!z^KRNz)|E>FSlKHrZ|fxPz1D@b@o{2q>Yu*-S9rQf9@>EwYCmvwl{eZ w1NQ%n*Tn~~=Fk|E#>B;tqmzmKNiBsL}*wov* zs#;67uaGp9yu5&Cd2V(npdq;H(xJp=n}}K{)r)DFHf*U$?zUU^tEe(+pNepkIdBKk zW$*>3GWB+ODqAeMy$H@DqkXHd@H8AN?UQ%qy1;o!5-sTKf$ m^QJ;w45=z#(t=36+GEMK38B0~#`|h2Lt_)Dwz*sAIW2XA zFDn_X&RP%?rI5~M5p~Vv^ *tj=r6zljC^?dWQk9dy63kml0 z#r5Uy-@RGew%w`}hBOxCcxev0zRLd0tsCkUGeM0mJFcMa2X-d(GI6M)Hlbuu-c7NC z K4Tmt1n}shGCsS>7z}W%%>O^-C>x zO6KtO+s|#i6=qlWhj)`WW>-b*4)pWrO5x+Xdb&5x%@}yjgaso@Hjc5`DSn$ndLpAA zUY;dQ)J(W}=I67h*trbnr64V41Oan8|D?MurG}UkSF&-%A2@LBC9(bh1Xd`03>Y)@ z`xoi~l;!plAZ$74)&(F%Aqe#B(w|yS-~XXQKfZF-6u1=S{2-tMNwSKKhmeAS%TGUC z{HrDlgz8X|0})k>Io%xN7snhu1lrsmJ}~jS!y2|tHB(&shnv5gO(XW7w0r9hBKGMl z1rUY7|MvNMpw@pW%71;!ScN0xA$-=eoGiRQb zmX5XxMuA4A+#!cc>b9!oAG? F2;Tkv75HQ<#Er+1~Q!iJVSE`?}=r|LPt} zceA2rFr1hTw393eWTY_|YIHPg%GnBGkfKpf`i6dOXkWR)2NhB`GCX{?-_(>8mxA$` zYur%Y4Wi+|c2}`N?GnQ5l2#3t>^&Fp*qphwc`Kw9O6i!n7xME4>5(rC1)Etu;&YSS zv^qc|zoa}i!rDqGlGP*Vg%ZEX!S@>{XC$ReGdB|-lGMpk*@9CKF+Wkgd63a}Sfmwn z{L#hYZ?hkJ4%{c5x1*!U;qLVQUi_ZOx *?O2U@RhA*g!LJOtm*6P2fRrOCijexVjP8 zowOF@Vd3T!T+X&xp2;LbojI^1U0p5Zw={Q-({9yF602rhBU0 Ys}p=>5|yjoF6D0+;M;9Td^@F%qFXco?g5_SVf6y zPteC#6tyat*-B1a2WQv^%ZqMmK<-OkgU)T|VUDdiVI1_8c0=;P%u{lR-RADOkhyCk zp+P}>d@7whbHa@A#`(N(_~w^jg$c$2A?u08WeIF`mau(wWtqnkK4OGPeR6&J8fAIX z=I)5&WUVXVa4Z>Hu{(bi |6+IIi@}+4GHOyPL5CL(RFGa zM#=u(_gZ68u2?A-k9afC;`EUM{Hq}07h=ayl_V|wXDDRKE}yCWJ|WqeX6Qty(1qtE zRp`oBe-!^Jig|tb4EHWg)$G8+!XkTla_k4007VLNbn^`tk4w6UZxZzlZzqE=(Q)UI zq&D)GQzRLVv0s?%P9_z1 @?^BdlZlmI znsc(CdOK+2#YhTzdiQ-vGuzZ#PjUdEul{=;vZXy8x68*X+fX?1{_FJ8-4ZqM@8kF7 zf96BMpZQ>ja`dH?{%Z#0oU-^UG`y8MG4=Bxz_a64X7D*`7QuZreI+J=5$H9}QO~#w zRzEGMtOjI1Zkq7>meDVFk8iNvB*qO4rOx=^W_%X#GFM5G&?VwfiqVNRuTY_5E}x6B z3^e8InjO*BZ`o1fWq0SQN-suM9JbwRxLPaNw;oVqFl5@X^3v3B!$kfbFhRQ&e`L7tXMGx)s<0bhQo$<%I+WJ|}wST2Ku@Wa%KfXC~H972vy z^-l)qBZ2hC%C;J3wrqRrJ4}L^1(U1Fe@c0M(+?c$Q{;KcmYg7GI6b)+BeU1~u>b4m zeJ8!s36gN7z ^o zhXC_l?W7!F%_@hxUCB4Qm`cMexDXA(CVM$ZZK}NPx%>)~?Qmybq??cx@C29RSFs#X zw3 EE98^YddISY5c;5Ov`@ z2t?2HO!3L96a~5Qb)6u62r@cIN4`22fo?fUD4B~=@gx1fkLUWm?oo(EM84S_Fsw@L zn?`yM k zaxLK{o9F*&q0AdrvwutdXNBYzZw0Ygt%OLn^KV8ya`Bp-i`Z2`CZ{A+dU9su5)Nlh zp@qYYHh#QQKkeC{n 3L_81C9xm11?C=|?!^ea-4mhKHE{Ov zseYvC>E(>RQ}oWw=imj|&&@;i5ogN9_23CR^~47I8SxLs)>dD7g<<7VKRYG_oO(8B zlIDd?`o7+SS_|hMZcqf9);#H59oji;cfz`5vCa5dR_}7_PM-bo^<4f&$S`x6!yESm zN8 w5!^X}rUbeEtrV6aY`$F+G+Frt^k*zCXGCIMfcwzTxm@}()Y z)zYLg=6Q-A=&d_g myHb-ZiER=lJfT0CAqJgywC_L1@Wkn%}+7xz%j zfL4q}g0>vPSE_2Cdk`BxGzw`sU%EDR7eDHMqKsw#ow`t7K0G{KUJ-;U$3 666IZduyD0W-=JyQCZEN<`;4-s;-6u8?eCo{u%Sk} zF FGaniy}&rb9Kw)FKc=V|IWPvOJ^jPLA`Q_V3Ju|Me+EaJKTJH2hzR{ro6 7P}S|R2G$#8bMGXuuYiY?;|*x*8N=)}IBJSnnyiQ-2Qxdx z=5ZV{0tEu