|
@@ -0,0 +1,591 @@
|
|
|
|
|
+# 报价自动分配功能需求文档 (PRD)
|
|
|
|
|
+
|
|
|
|
|
+## 一、功能概述
|
|
|
|
|
+
|
|
|
|
|
+### 1.1 功能定位
|
|
|
|
|
+报价自动分配功能是映三色视觉设计表现公司报价系统的核心优化模块,实现从客户报价到内部执行的自动化价格拆分与分配。
|
|
|
|
|
+
|
|
|
|
|
+### 1.2 业务价值
|
|
|
|
|
+- **提升效率**: 自动化价格分配,减少人工计算时间
|
|
|
|
|
+- **标准化流程**: 统一内部执行报价标准,避免人为偏差
|
|
|
|
|
+- **透明管理**: 清晰展示价格构成,便于财务核算和绩效考核
|
|
|
|
|
+- **利润保障**: 确保公司分配占比,保障企业利润空间
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 二、核心业务规则
|
|
|
|
|
+
|
|
|
|
|
+### 2.1 内部执行报价三级分配体系
|
|
|
|
|
+
|
|
|
|
|
+基于**三级报价总价**(一级/二级/三级客户报价),自动按以下比例拆分:
|
|
|
|
|
+
|
|
|
|
|
+| 分配类型 | 占比 | 说明 | 用途 |
|
|
|
|
|
+|---------|------|------|------|
|
|
|
|
|
+| **建模阶段** | 10% | 3D模型构建阶段 | 支付给建模设计师的费用 |
|
|
|
|
|
+| **软装渲染** | 40% | 软装搭配+效果图渲染 | 支付给软装和渲染设计师的费用 |
|
|
|
|
|
+| **公司分配** | 50% | 公司运营与利润 | 公司管理成本、平台服务费、利润等 |
|
|
|
|
|
+
|
|
|
|
|
+**计算公式:**
|
|
|
|
|
+```
|
|
|
|
|
+建模阶段金额 = 三级报价总价 × 10%
|
|
|
|
|
+软装渲染金额 = 三级报价总价 × 40%
|
|
|
|
|
+公司分配金额 = 三级报价总价 × 50%
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 2.2 分配示例
|
|
|
|
|
+
|
|
|
|
|
+#### 示例1: 家装平层项目
|
|
|
|
|
+- **项目类型**: 家装平层(现代风格)
|
|
|
|
|
+- **报价等级**: 三级
|
|
|
|
|
+- **三级报价总价**: ¥60,000
|
|
|
|
|
+
|
|
|
|
|
+**自动分配结果:**
|
|
|
|
|
+```
|
|
|
|
|
+建模阶段: ¥60,000 × 10% = ¥6,000
|
|
|
|
|
+软装渲染: ¥60,000 × 40% = ¥24,000
|
|
|
|
|
+公司分配: ¥60,000 × 50% = ¥30,000
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 示例2: 工装办公空间
|
|
|
|
|
+- **项目类型**: 工装办公空间
|
|
|
|
|
+- **报价等级**: 二级
|
|
|
|
|
+- **三级报价总价**: ¥85,000
|
|
|
|
|
+
|
|
|
|
|
+**自动分配结果:**
|
|
|
|
|
+```
|
|
|
|
|
+建模阶段: ¥85,000 × 10% = ¥8,500
|
|
|
|
|
+软装渲染: ¥85,000 × 40% = ¥34,000
|
|
|
|
|
+公司分配: ¥85,000 × 50% = ¥42,500
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 三、数据结构设计
|
|
|
|
|
+
|
|
|
|
|
+### 3.1 报价数据结构扩展
|
|
|
|
|
+
|
|
|
|
|
+在现有 `quotation` 对象基础上,新增 `allocation` 字段:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+interface Quotation {
|
|
|
|
|
+ spaces: Space[]; // 产品设计产品列表
|
|
|
|
|
+ total: number; // 报价总额
|
|
|
|
|
+ spaceBreakdown: SpaceBreakdown[]; // 产品占比明细
|
|
|
|
|
+
|
|
|
|
|
+ // 新增: 内部执行分配
|
|
|
|
|
+ allocation?: {
|
|
|
|
|
+ modeling: {
|
|
|
|
|
+ amount: number; // 建模阶段金额 (10%)
|
|
|
|
|
+ percentage: 10; // 固定占比
|
|
|
|
|
+ description: string; // '3D模型构建'
|
|
|
|
|
+ };
|
|
|
|
|
+ decoration: {
|
|
|
|
|
+ amount: number; // 软装渲染金额 (40%)
|
|
|
|
|
+ percentage: 40; // 固定占比
|
|
|
|
|
+ description: string; // '软装搭配+效果图渲染'
|
|
|
|
|
+ };
|
|
|
|
|
+ company: {
|
|
|
|
|
+ amount: number; // 公司分配金额 (50%)
|
|
|
|
|
+ percentage: 50; // 固定占比
|
|
|
|
|
+ description: string; // '公司运营与利润'
|
|
|
|
|
+ };
|
|
|
|
|
+ updatedAt: Date; // 分配计算时间
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ generatedAt: Date;
|
|
|
|
|
+ validUntil: Date;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 3.2 Product 报价数据扩展
|
|
|
|
|
+
|
|
|
|
|
+每个产品的 `quotation` 对象也需要支持分配信息:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+interface ProductQuotation {
|
|
|
|
|
+ price: number; // 产品报价
|
|
|
|
|
+ currency: string; // 货币类型
|
|
|
|
|
+ breakdown: ProcessBreakdown; // 工序明细
|
|
|
|
|
+
|
|
|
|
|
+ // 新增: 产品级别的内部分配
|
|
|
|
|
+ allocation?: {
|
|
|
|
|
+ modeling: number; // 该产品的建模阶段金额
|
|
|
|
|
+ decoration: number; // 该产品的软装渲染金额
|
|
|
|
|
+ company: number; // 该产品的公司分配金额
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ status: string;
|
|
|
|
|
+ validUntil: Date;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 四、功能实现方案
|
|
|
|
|
+
|
|
|
|
|
+### 4.1 自动分配触发时机
|
|
|
|
|
+
|
|
|
|
|
+自动分配在以下时机触发:
|
|
|
|
|
+
|
|
|
|
|
+1. **生成报价时**: 点击"生成报价"按钮后,自动计算分配
|
|
|
|
|
+2. **修改价格时**: 工序价格或数量变化导致总价变化时,实时重新计算
|
|
|
|
|
+3. **保存报价时**: 保存报价前,确保分配数据最新
|
|
|
|
|
+
|
|
|
|
|
+### 4.2 分配计算逻辑
|
|
|
|
|
+
|
|
|
|
|
+#### 步骤1: 计算项目总价
|
|
|
|
|
+```typescript
|
|
|
|
|
+function calculateTotal(): number {
|
|
|
|
|
+ let total = 0;
|
|
|
|
|
+ for (const space of quotation.spaces) {
|
|
|
|
|
+ for (const processKey of Object.keys(space.processes)) {
|
|
|
|
|
+ const process = space.processes[processKey];
|
|
|
|
|
+ if (process.enabled) {
|
|
|
|
|
+ total += process.price * process.quantity;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return total;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 步骤2: 自动分配内部执行金额
|
|
|
|
|
+```typescript
|
|
|
|
|
+function calculateAllocation(total: number) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ modeling: {
|
|
|
|
|
+ amount: Math.round(total * 0.10), // 10%
|
|
|
|
|
+ percentage: 10,
|
|
|
|
|
+ description: '3D模型构建'
|
|
|
|
|
+ },
|
|
|
|
|
+ decoration: {
|
|
|
|
|
+ amount: Math.round(total * 0.40), // 40%
|
|
|
|
|
+ percentage: 40,
|
|
|
|
|
+ description: '软装搭配+效果图渲染'
|
|
|
|
|
+ },
|
|
|
|
|
+ company: {
|
|
|
|
|
+ amount: Math.round(total * 0.50), // 50%
|
|
|
|
|
+ percentage: 50,
|
|
|
|
|
+ description: '公司运营与利润'
|
|
|
|
|
+ },
|
|
|
|
|
+ updatedAt: new Date()
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 步骤3: 产品级别分配
|
|
|
|
|
+```typescript
|
|
|
|
|
+function calculateProductAllocation(productPrice: number) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ modeling: Math.round(productPrice * 0.10),
|
|
|
|
|
+ decoration: Math.round(productPrice * 0.40),
|
|
|
|
|
+ company: Math.round(productPrice * 0.50)
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4.3 UI展示设计
|
|
|
|
|
+
|
|
|
|
|
+#### 位置1: 报价汇总区域
|
|
|
|
|
+在现有的"报价汇总"模块中,新增"内部执行分配"折叠面板:
|
|
|
|
|
+
|
|
|
|
|
+```html
|
|
|
|
|
+<!-- 报价汇总 -->
|
|
|
|
|
+<div class="quotation-summary">
|
|
|
|
|
+ <!-- 现有的产品占比明细 -->
|
|
|
|
|
+ ...
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 新增: 内部执行分配 -->
|
|
|
|
|
+ <div class="allocation-section">
|
|
|
|
|
+ <div class="allocation-header">
|
|
|
|
|
+ <h4>内部执行分配</h4>
|
|
|
|
|
+ <button (click)="toggleAllocation()">
|
|
|
|
|
+ {{ showAllocation ? '隐藏' : '显示' }}
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ @if (showAllocation && quotation.allocation) {
|
|
|
|
|
+ <div class="allocation-list">
|
|
|
|
|
+ <div class="allocation-item modeling">
|
|
|
|
|
+ <div class="allocation-info">
|
|
|
|
|
+ <span class="allocation-name">建模阶段</span>
|
|
|
|
|
+ <span class="allocation-desc">3D模型构建</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="allocation-values">
|
|
|
|
|
+ <span class="allocation-percentage">10%</span>
|
|
|
|
|
+ <span class="allocation-amount">{{ formatPrice(quotation.allocation.modeling.amount) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="allocation-item decoration">
|
|
|
|
|
+ <div class="allocation-info">
|
|
|
|
|
+ <span class="allocation-name">软装渲染</span>
|
|
|
|
|
+ <span class="allocation-desc">软装搭配+效果图渲染</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="allocation-values">
|
|
|
|
|
+ <span class="allocation-percentage">40%</span>
|
|
|
|
|
+ <span class="allocation-amount">{{ formatPrice(quotation.allocation.decoration.amount) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="allocation-item company">
|
|
|
|
|
+ <div class="allocation-info">
|
|
|
|
|
+ <span class="allocation-name">公司分配</span>
|
|
|
|
|
+ <span class="allocation-desc">公司运营与利润</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="allocation-values">
|
|
|
|
|
+ <span class="allocation-percentage">50%</span>
|
|
|
|
|
+ <span class="allocation-amount">{{ formatPrice(quotation.allocation.company.amount) }}</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ }
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 报价总额 -->
|
|
|
|
|
+ <div class="total-section">
|
|
|
|
|
+ ...
|
|
|
|
|
+ </div>
|
|
|
|
|
+</div>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 位置2: 产品详情(可选)
|
|
|
|
|
+在每个产品卡片的工序明细下方,可展示该产品的分配明细:
|
|
|
|
|
+
|
|
|
|
|
+```html
|
|
|
|
|
+<div class="product-allocation-hint">
|
|
|
|
|
+ <svg class="icon" viewBox="0 0 512 512">...</svg>
|
|
|
|
|
+ <span>该产品内部分配: 建模 ¥{{ product.allocation.modeling }} / 软装渲染 ¥{{ product.allocation.decoration }} / 公司 ¥{{ product.allocation.company }}</span>
|
|
|
|
|
+</div>
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 五、样式设计
|
|
|
|
|
+
|
|
|
|
|
+### 5.1 分配列表样式
|
|
|
|
|
+
|
|
|
|
|
+```scss
|
|
|
|
|
+.allocation-section {
|
|
|
|
|
+ margin-top: 20px;
|
|
|
|
|
+ padding-top: 20px;
|
|
|
|
|
+ border-top: 1px solid var(--ion-color-light-shade);
|
|
|
|
|
+
|
|
|
|
|
+ .allocation-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
|
+
|
|
|
|
|
+ h4 {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--ion-color-dark);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .allocation-list {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+
|
|
|
|
|
+ .allocation-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 12px 16px;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ border-left: 4px solid;
|
|
|
|
|
+ background: var(--ion-color-light-tint);
|
|
|
|
|
+
|
|
|
|
|
+ &.modeling {
|
|
|
|
|
+ border-left-color: #8B5CF6; // 紫色 - 建模
|
|
|
|
|
+ background: rgba(139, 92, 246, 0.05);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.decoration {
|
|
|
|
|
+ border-left-color: #F59E0B; // 橙色 - 软装渲染
|
|
|
|
|
+ background: rgba(245, 158, 11, 0.05);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ &.company {
|
|
|
|
|
+ border-left-color: #10B981; // 绿色 - 公司分配
|
|
|
|
|
+ background: rgba(16, 185, 129, 0.05);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .allocation-info {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 4px;
|
|
|
|
|
+
|
|
|
|
|
+ .allocation-name {
|
|
|
|
|
+ font-size: 15px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: var(--ion-color-dark);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .allocation-desc {
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: var(--ion-color-medium);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .allocation-values {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: baseline;
|
|
|
|
|
+ gap: 12px;
|
|
|
|
|
+
|
|
|
|
|
+ .allocation-percentage {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ color: var(--ion-color-medium);
|
|
|
|
|
+ min-width: 40px;
|
|
|
|
|
+ text-align: right;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .allocation-amount {
|
|
|
|
|
+ font-size: 18px;
|
|
|
|
|
+ font-weight: 700;
|
|
|
|
|
+ color: var(--ion-color-dark);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 5.2 移动端适配
|
|
|
|
|
+
|
|
|
|
|
+```scss
|
|
|
|
|
+@media (max-width: 768px) {
|
|
|
|
|
+ .allocation-item {
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: flex-start !important;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+
|
|
|
|
|
+ .allocation-values {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 六、权限控制
|
|
|
|
|
+
|
|
|
|
|
+### 6.1 可见性权限
|
|
|
|
|
+- **全部角色可见**: 内部执行分配信息对内部所有角色可见
|
|
|
|
|
+- **客户不可见**: 外部客户看到的报价中,不包含内部分配信息
|
|
|
|
|
+
|
|
|
|
|
+### 6.2 编辑权限
|
|
|
|
|
+- **系统自动计算**: 内部分配比例(10%-40%-50%)为系统固定,不支持手动修改
|
|
|
|
|
+- **管理员可调整**: 仅管理员可在系统配置中调整分配比例(未来扩展功能)
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 七、技术实现要点
|
|
|
|
|
+
|
|
|
|
|
+### 7.1 配置文件更新
|
|
|
|
|
+
|
|
|
|
|
+在 `quotation-rules.ts` 中新增分配规则配置:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+/**
|
|
|
|
|
+ * 内部执行分配规则
|
|
|
|
|
+ */
|
|
|
|
|
+export const ALLOCATION_RULES = {
|
|
|
|
|
+ modeling: {
|
|
|
|
|
+ percentage: 0.10,
|
|
|
|
|
+ label: '建模阶段',
|
|
|
|
|
+ description: '3D模型构建',
|
|
|
|
|
+ color: '#8B5CF6'
|
|
|
|
|
+ },
|
|
|
|
|
+ decoration: {
|
|
|
|
|
+ percentage: 0.40,
|
|
|
|
|
+ label: '软装渲染',
|
|
|
|
|
+ description: '软装搭配+效果图渲染',
|
|
|
|
|
+ color: '#F59E0B'
|
|
|
|
|
+ },
|
|
|
|
|
+ company: {
|
|
|
|
|
+ percentage: 0.50,
|
|
|
|
|
+ label: '公司分配',
|
|
|
|
|
+ description: '公司运营与利润',
|
|
|
|
|
+ color: '#10B981'
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 计算内部执行分配
|
|
|
|
|
+ */
|
|
|
|
|
+export function calculateAllocation(totalPrice: number) {
|
|
|
|
|
+ return {
|
|
|
|
|
+ modeling: {
|
|
|
|
|
+ amount: Math.round(totalPrice * ALLOCATION_RULES.modeling.percentage),
|
|
|
|
|
+ percentage: ALLOCATION_RULES.modeling.percentage * 100,
|
|
|
|
|
+ description: ALLOCATION_RULES.modeling.description,
|
|
|
|
|
+ color: ALLOCATION_RULES.modeling.color
|
|
|
|
|
+ },
|
|
|
|
|
+ decoration: {
|
|
|
|
|
+ amount: Math.round(totalPrice * ALLOCATION_RULES.decoration.percentage),
|
|
|
|
|
+ percentage: ALLOCATION_RULES.decoration.percentage * 100,
|
|
|
|
|
+ description: ALLOCATION_RULES.decoration.description,
|
|
|
|
|
+ color: ALLOCATION_RULES.decoration.color
|
|
|
|
|
+ },
|
|
|
|
|
+ company: {
|
|
|
|
|
+ amount: Math.round(totalPrice * ALLOCATION_RULES.company.percentage),
|
|
|
|
|
+ percentage: ALLOCATION_RULES.company.percentage * 100,
|
|
|
|
|
+ description: ALLOCATION_RULES.company.description,
|
|
|
|
|
+ color: ALLOCATION_RULES.company.color
|
|
|
|
|
+ },
|
|
|
|
|
+ updatedAt: new Date()
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 7.2 组件方法扩展
|
|
|
|
|
+
|
|
|
|
|
+在 `QuotationEditorComponent` 中新增方法:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// UI 状态
|
|
|
|
|
+showAllocation: boolean = false;
|
|
|
|
|
+
|
|
|
|
|
+// 计算总价时同步更新分配
|
|
|
|
|
+calculateTotal() {
|
|
|
|
|
+ // ... 现有逻辑 ...
|
|
|
|
|
+
|
|
|
|
|
+ // 自动计算内部执行分配
|
|
|
|
|
+ this.quotation.allocation = calculateAllocation(this.quotation.total);
|
|
|
|
|
+ this.updateProductBreakdown();
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 切换分配显示
|
|
|
|
|
+toggleAllocation() {
|
|
|
|
|
+ this.showAllocation = !this.showAllocation;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 更新产品级别的分配
|
|
|
|
|
+private updateProductAllocation(product: any, productPrice: number) {
|
|
|
|
|
+ const quotation = product.get('quotation') || {};
|
|
|
|
|
+ quotation.allocation = {
|
|
|
|
|
+ modeling: Math.round(productPrice * 0.10),
|
|
|
|
|
+ decoration: Math.round(productPrice * 0.40),
|
|
|
|
|
+ company: Math.round(productPrice * 0.50)
|
|
|
|
|
+ };
|
|
|
|
|
+ product.set('quotation', quotation);
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 八、测试用例
|
|
|
|
|
+
|
|
|
|
|
+### 8.1 单元测试
|
|
|
|
|
+
|
|
|
|
|
+#### 测试用例1: 分配计算准确性
|
|
|
|
|
+```typescript
|
|
|
|
|
+test('should calculate allocation correctly', () => {
|
|
|
|
|
+ const total = 60000;
|
|
|
|
|
+ const allocation = calculateAllocation(total);
|
|
|
|
|
+
|
|
|
|
|
+ expect(allocation.modeling.amount).toBe(6000); // 10%
|
|
|
|
|
+ expect(allocation.decoration.amount).toBe(24000); // 40%
|
|
|
|
|
+ expect(allocation.company.amount).toBe(30000); // 50%
|
|
|
|
|
+});
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 测试用例2: 四舍五入处理
|
|
|
|
|
+```typescript
|
|
|
|
|
+test('should round allocation amounts', () => {
|
|
|
|
|
+ const total = 12345;
|
|
|
|
|
+ const allocation = calculateAllocation(total);
|
|
|
|
|
+
|
|
|
|
|
+ expect(allocation.modeling.amount).toBe(1235); // 10% rounded
|
|
|
|
|
+ expect(allocation.decoration.amount).toBe(4938); // 40% rounded
|
|
|
|
|
+ expect(allocation.company.amount).toBe(6173); // 50% rounded
|
|
|
|
|
+});
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 8.2 集成测试
|
|
|
|
|
+
|
|
|
|
|
+#### 测试用例3: 生成报价后自动计算分配
|
|
|
|
|
+```typescript
|
|
|
|
|
+test('should auto-calculate allocation after generating quotation', async () => {
|
|
|
|
|
+ const component = new QuotationEditorComponent(...);
|
|
|
|
|
+ await component.generateQuotationFromProducts();
|
|
|
|
|
+
|
|
|
|
|
+ expect(component.quotation.allocation).toBeDefined();
|
|
|
|
|
+ expect(component.quotation.allocation.modeling.amount).toBeGreaterThan(0);
|
|
|
|
|
+});
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 九、用户使用流程
|
|
|
|
|
+
|
|
|
|
|
+### 9.1 报价生成流程
|
|
|
|
|
+
|
|
|
|
|
+1. 客服在项目详情页打开报价编辑器
|
|
|
|
|
+2. 点击"生成报价"按钮,系统自动:
|
|
|
|
|
+ - 根据产品列表生成工序明细
|
|
|
|
|
+ - 计算报价总额
|
|
|
|
|
+ - **自动计算内部执行分配 (10%-40%-50%)**
|
|
|
|
|
+ - 保存到项目数据
|
|
|
|
|
+3. 在"报价汇总"区域查看:
|
|
|
|
|
+ - 报价总额
|
|
|
|
|
+ - 产品占比明细
|
|
|
|
|
+ - **内部执行分配明细(新增)**
|
|
|
|
|
+
|
|
|
|
|
+### 9.2 分配信息查看流程
|
|
|
|
|
+
|
|
|
|
|
+1. 展开"报价汇总"模块
|
|
|
|
|
+2. 点击"内部执行分配"区域的"显示"按钮
|
|
|
|
|
+3. 查看三类分配及其金额:
|
|
|
|
|
+ - 建模阶段: 10% + 具体金额
|
|
|
|
|
+ - 软装渲染: 40% + 具体金额
|
|
|
|
|
+ - 公司分配: 50% + 具体金额
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 十、未来扩展
|
|
|
|
|
+
|
|
|
|
|
+### 10.1 动态比例配置(Phase 2)
|
|
|
|
|
+- 支持管理员在系统设置中调整分配比例
|
|
|
|
|
+- 支持不同项目类型设置不同分配规则
|
|
|
|
|
+- 支持历史分配规则版本管理
|
|
|
|
|
+
|
|
|
|
|
+### 10.2 设计师绩效对接(Phase 3)
|
|
|
|
|
+- 建模设计师绩效 = 所有项目的建模阶段金额总和
|
|
|
|
|
+- 软装渲染设计师绩效 = 所有项目的软装渲染金额总和
|
|
|
|
|
+- 自动生成设计师收入报表
|
|
|
|
|
+
|
|
|
|
|
+### 10.3 财务报表集成(Phase 4)
|
|
|
|
|
+- 自动生成内部分配财务报表
|
|
|
|
|
+- 支持按部门、按时间段统计分配金额
|
|
|
|
|
+- 支持导出Excel财务报表
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## 十一、FAQ
|
|
|
|
|
+
|
|
|
|
|
+### Q1: 分配比例是否可以手动调整?
|
|
|
|
|
+**A:** 当前版本不支持。内部分配比例(10%-40%-50%)为系统固定规则,确保所有项目的一致性和公平性。
|
|
|
|
|
+
|
|
|
|
|
+### Q2: 如果总价有小数,如何处理分配金额?
|
|
|
|
|
+**A:** 使用 `Math.round()` 四舍五入到整数。例如: ¥12345 × 10% = ¥1234.5 → ¥1235
|
|
|
|
|
+
|
|
|
|
|
+### Q3: 客户是否能看到内部分配信息?
|
|
|
|
|
+**A:** 不能。内部分配仅在系统内部显示,客户看到的报价只包含产品和工序明细,不包含内部分配。
|
|
|
|
|
+
|
|
|
|
|
+### Q4: 分配信息何时更新?
|
|
|
|
|
+**A:** 每次报价总额发生变化时(生成报价、修改工序价格、保存报价),系统自动重新计算分配。
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+**文档版本**: v1.0
|
|
|
|
|
+**创建日期**: 2025-10-25
|
|
|
|
|
+**作者**: Claude Code AI
|
|
|
|
|
+**审核状态**: 待审核
|