From bee96a1d36c86068cd5a7eb69f4e3294a8123b04 Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期五, 03 七月 2026 18:02:55 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/dev_business' into dev_business
---
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java | 18
sql/mysql/mes_auto_code_rule_init.sql | 523 +++++++++++++++
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java | 30
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java | 6
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/CrmSaleQuotationController.java | 37
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/contract/CrmContractApiImpl.java | 39 +
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/report/CrmReportApiImpl.java | 2
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java | 4
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java | 156 ----
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java | 14
docs/crm_erp_integration_frontend.md | 199 ++++++
docs/contract_approval_change.md | 271 ++++++++
docs/contract_product_field_change.md | 211 ++++++
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java | 12
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java | 4
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java | 128 +++
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java | 65 +
.claude/rules/sql-script.md | 6
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java | 11
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractStatusListener.java | 45 +
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java | 4
yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/contract/CrmContractApi.java | 30
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java | 5
/dev/null | 56 -
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/quotation/CrmSaleQuotationStatusEnum.java | 10
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationService.java | 25
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java | 3
.claude/rules/compile-check.md | 2
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractSaveReqVO.java | 4
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java | 6
30 files changed, 1,633 insertions(+), 293 deletions(-)
diff --git a/.claude/rules/compile-check.md b/.claude/rules/compile-check.md
index 7ef5049..46227e5 100644
--- a/.claude/rules/compile-check.md
+++ b/.claude/rules/compile-check.md
@@ -13,6 +13,8 @@
| CRM 妯″潡 | MdmItemApi | MDM 鐗╂枡鎺ュ彛 |
| CRM 妯″潡 | MdmUnitMeasureApi | MDM 璁¢噺鍗曚綅鎺ュ彛 |
| CRM 妯″潡 | BpmProcessInstanceApi | BPM 娴佺▼瀹炰緥鎺ュ彛 |
+| CRM 妯″潡 | BpmCategoryService | BPM 鍒嗙被鏈嶅姟 |
+| CRM 妯″潡 | BpmProcessDefinitionService | BPM 娴佺▼瀹氫箟鏈嶅姟 |
| ERP 妯″潡 | CrmCustomerApi | CRM 瀹㈡埛鎺ュ彛 |
| ERP 妯″潡 | MdmItemApi | MDM 鐗╂枡鎺ュ彛 |
diff --git a/.claude/rules/sql-script.md b/.claude/rules/sql-script.md
new file mode 100644
index 0000000..19b1ccb
--- /dev/null
+++ b/.claude/rules/sql-script.md
@@ -0,0 +1,6 @@
+# SQL 鑴氭湰瑙勮寖
+
+## 瑙勫垯
+
+- SQL 鑴氭湰涓笉瑕佸寘鍚� tenant_id 瀛楁锛岄」鐩笉浣跨敤澶氱鎴锋ā寮忋��
+- INSERT 璇彞鍙寘鍚繀瑕佺殑涓氬姟瀛楁锛屼笉闇�瑕� tenant_id銆�
\ No newline at end of file
diff --git a/docs/contract_approval_change.md b/docs/contract_approval_change.md
new file mode 100644
index 0000000..03b35cb
--- /dev/null
+++ b/docs/contract_approval_change.md
@@ -0,0 +1,271 @@
+# 鍚堝悓瀹℃壒娴佺▼鍙樻洿
+
+## 鍙樻洿璇存槑
+
+1. **閿�鍞姤浠峰彇娑� BPM 瀹℃牳**锛氶攢鍞姤浠峰崟涓嶅啀璧板鎵规祦绋嬶紝绠�鍖栦负鑽夌 鈫� 宸茶浆鍚堝悓涓ょ鐘舵��
+2. **鍚堝悓瀹℃壒鍔ㄦ�侀�夋嫨娴佺▼**锛氬垱寤哄悎鍚屽悗锛岄渶鐢ㄦ埛鎵嬪姩閫夋嫨瀹℃壒娴佺▼骞舵彁浜ゅ鎵癸紙绫讳技閲囪喘鐢宠锛�
+
+---
+
+## 鏁版嵁搴撳彉鏇� SQL
+
+```sql
+-- =============================================
+-- 1. 鍚堝悓浜у搧琛ㄥ瓧娈靛彉鏇达紙绉婚櫎鏃у瓧娈碉級
+-- =============================================
+ALTER TABLE `crm_contract_product` DROP COLUMN `product_id`;
+ALTER TABLE `crm_contract_product` DROP COLUMN `product_price`;
+
+-- =============================================
+-- 2. BPM 鍒嗙被鍒涘缓锛堝悎鍚屽鎵癸級
+-- =============================================
+INSERT INTO `bpm_category` (`name`, `code`, `description`, `status`, `sort`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+VALUES ('鍚堝悓瀹℃壒', 'contract_approve', '鍚堝悓瀹℃壒娴佺▼鍒嗙被', 0, 0, '1', NOW(), '1', NOW(), b'0');
+
+-- =============================================
+-- 3. 閿�鍞姤浠风姸鎬佸瓧娈垫洿鏂帮紙绠�鍖栫姸鎬侊級
+-- =============================================
+-- 鐘舵�佸�煎彉鏇达細0=鑽夌, 10=宸茶浆鍚堝悓
+-- 鍘� status 瀛楁鍊奸渶瑕佹竻鐞嗭細10(瀹℃壒涓�)銆�20(宸查�氳繃)銆�30(宸叉嫆缁�)銆�40(宸插彇娑�) 鏀逛负 0(鑽夌)
+UPDATE `crm_sale_quotation` SET `status` = 0 WHERE `status` IN (10, 20, 30, 40);
+-- 宸茶浆鍚堝悓鐨勪繚鎸佷笉鍙橈紙status=50 鏀逛负 status=10锛�
+UPDATE `crm_sale_quotation` SET `status` = 10 WHERE `status` = 50;
+
+-- =============================================
+-- 4. 鍚堝悓瀹℃壒鐘舵�佸瓧娈垫洿鏂帮紙璁剧疆榛樿鍊硷級
+-- =============================================
+-- 灏� audit_status 涓� NULL 鐨勮褰曡缃负 0锛堟湭鎻愪氦锛�
+UPDATE `crm_contract` SET `audit_status` = 0 WHERE `audit_status` IS NULL;
+
+-- =============================================
+-- 4. 鎶ヤ环鍗曚骇鍝佹槑缁嗚〃锛堝鏋滆〃涓嶅瓨鍦ㄩ渶鍒涘缓锛屽凡瀛樺湪鍒欒ˉ鍏呭瓧娈碉級
+-- =============================================
+-- 濡傛灉 crm_sale_quotation_product 琛ㄤ笉瀛樺湪锛屾墽琛屽垱寤�
+CREATE TABLE IF NOT EXISTS `crm_sale_quotation_product` (
+ `id` bigint NOT NULL AUTO_INCREMENT COMMENT '缂栧彿',
+ `quotation_id` bigint NOT NULL COMMENT '鎶ヤ环鍗曠紪鍙�',
+ `item_id` bigint NOT NULL COMMENT 'MDM鐗╂枡缂栧彿',
+ `item_unit_id` bigint DEFAULT NULL COMMENT '璁¢噺鍗曚綅缂栧彿',
+ `item_price` decimal(24,6) NOT NULL DEFAULT 0 COMMENT '鐗╂枡鍘熶环锛堝啑浣欙級',
+ `quotation_price` decimal(24,6) NOT NULL COMMENT '鎶ヤ环鍗曚环',
+ `count` decimal(24,6) NOT NULL COMMENT '鏁伴噺',
+ `total_price` decimal(24,6) NOT NULL COMMENT '灏忚閲戦',
+ `tax_percent` decimal(10,2) DEFAULT NULL COMMENT '绋庣巼(%)',
+ `tax_price` decimal(24,6) DEFAULT NULL COMMENT '绋庨',
+ `remark` varchar(256) DEFAULT NULL COMMENT '澶囨敞',
+ `creator` varchar(64) DEFAULT NULL COMMENT '鍒涘缓鑰�',
+ `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ `updater` varchar(64) DEFAULT NULL COMMENT '鏇存柊鑰�',
+ `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+ `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '鏄惁鍒犻櫎',
+ `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '绉熸埛缂栧彿',
+ PRIMARY KEY (`id`),
+ KEY `idx_quotation_id` (`quotation_id`)
+) ENGINE=InnoDB COMMENT='CRM 閿�鍞姤浠峰崟浜у搧鏄庣粏';
+
+-- 濡傛灉琛ㄥ凡瀛樺湪浣嗙己灏� item_price 瀛楁锛屾墽琛岃ˉ鍏�
+-- ALTER TABLE `crm_sale_quotation_product`
+-- ADD COLUMN `item_price` decimal(24,6) NOT NULL DEFAULT 0 COMMENT '鐗╂枡鍘熶环锛堝啑浣欙級' AFTER `item_unit_id`;
+```
+
+---
+
+## 娑夊強椤甸潰
+
+- 閿�鍞姤浠峰崟鍒楄〃椤甸潰
+- 閿�鍞姤浠峰崟琛ㄥ崟椤甸潰
+- 鍚堝悓鍒楄〃椤甸潰
+- 鍚堝悓璇︽儏椤甸潰
+
+---
+
+## API 鍙樻洿
+
+### 鍚堝悓鎺ュ彛鏂板
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | /crm/contract/approve-process-list | 鑾峰彇鍚堝悓瀹℃壒娴佺▼鍒楄〃 |
+| PUT | /crm/contract/submit | 鎻愪氦鍚堝悓瀹℃壒锛堥渶浼� processDefinitionKey锛� |
+
+**submit 鎺ュ彛鍙傛暟鍙樻洿锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| id | Long | 鏄� | 鍚堝悓缂栧彿 |
+| processDefinitionKey | String | 鏄� | 娴佺▼瀹氫箟 Key锛堢敤鎴烽�夋嫨锛� |
+
+### 閿�鍞姤浠峰崟鎺ュ彛鍙樻洿
+
+#### 绉婚櫎鐨勬帴鍙�
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| PUT | /crm/sale-quotation/submit | 鎻愪氦瀹℃壒锛堝凡绉婚櫎锛� |
+| GET | /crm/sale-quotation/approve-process-list | 鑾峰彇瀹℃壒娴佺▼鍒楄〃锛堝凡绉婚櫎锛� |
+
+#### 鐘舵�佸彉鏇�
+
+| 鏃х姸鎬佸�� | 鏃х姸鎬佸悕 | 鏂扮姸鎬佸�� | 鏂扮姸鎬佸悕 |
+|----------|----------|----------|----------|
+| 0 | 鑽夌 | 0 | 鑽夌 |
+| 10 | 瀹℃壒涓� | - | 宸茬Щ闄� |
+| 20 | 宸查�氳繃 | - | 宸茬Щ闄� |
+| 30 | 宸叉嫆缁� | - | 宸茬Щ闄� |
+| 40 | 宸插彇娑� | - | 宸茬Щ闄� |
+| 50 | 宸茶浆鍚堝悓 | 10 | 宸茶浆鍚堝悓 |
+
+---
+
+## 鍓嶇淇敼鐐�
+
+### 1. 鍚堝悓鍒楄〃椤甸潰 - 鎻愪氦瀹℃壒
+
+**鏂板鎻愪氦瀹℃壒寮圭獥锛�**
+
+```html
+<!-- 鎻愪氦瀹℃壒寮圭獥 -->
+<el-dialog title="閫夋嫨瀹℃壒娴佺▼" v-model="submitDialogVisible" width="400px">
+ <el-form label-width="100px">
+ <el-form-item label="瀹℃壒娴佺▼" required>
+ <el-select v-model="selectedProcessKey" placeholder="璇烽�夋嫨" style="width: 100%">
+ <el-option v-for="item in processList" :key="item.key" :label="item.name" :value="item.key" />
+ </el-select>
+ </el-form-item>
+ </el-form>
+ <template #footer>
+ <el-button @click="submitDialogVisible = false">鍙栨秷</el-button>
+ <el-button type="primary" @click="confirmSubmit" :loading="submitLoading">纭畾</el-button>
+ </template>
+</el-dialog>
+```
+
+**JS 鏂规硶锛�**
+
+```js
+import { getContractApproveProcessList, submitContract } from '@/api/crm/contract'
+
+methods: {
+ async handleSubmit(row) {
+ const { data } = await getContractApproveProcessList()
+ this.processList = data
+ this.currentRow = row
+ this.selectedProcessKey = null
+ this.submitDialogVisible = true
+ },
+ async confirmSubmit() {
+ if (!this.selectedProcessKey) {
+ this.$message.warning('璇烽�夋嫨瀹℃壒娴佺▼')
+ return
+ }
+ this.submitLoading = true
+ try {
+ await submitContract(this.currentRow.id, this.selectedProcessKey)
+ this.$message.success('鎻愪氦鎴愬姛')
+ this.submitDialogVisible = false
+ this.getList()
+ } finally {
+ this.submitLoading = false
+ }
+ }
+}
+```
+
+**鎿嶄綔鎸夐挳璋冩暣锛�**
+
+```html
+<el-table-column label="鎿嶄綔" width="280">
+ <template #default="{ row }">
+ <el-button v-if="row.auditStatus === 0" type="primary" size="small" @click="handleUpdate(row)">缂栬緫</el-button>
+ <el-button v-if="row.auditStatus === 0" type="success" size="small" @click="handleSubmit(row)">鎻愪氦瀹℃壒</el-button>
+ <el-button v-if="row.auditStatus === 0" type="danger" size="small" @click="handleDelete(row)">鍒犻櫎</el-button>
+ <el-button v-if="row.auditStatus === 20" type="warning" size="small" @click="handleGenerateOrder(row)">鐢熸垚璁㈠崟</el-button>
+ </template>
+</el-table-column>
+```
+
+### 2. 鍚堝悓鐘舵�佹樉绀�
+
+```html
+<!-- 鍚堝悓瀹℃壒鐘舵�� -->
+<el-tag v-if="row.auditStatus === 0" type="info">鏈彁浜�</el-tag>
+<el-tag v-if="row.auditStatus === 10" type="warning">瀹℃壒涓�</el-tag>
+<el-tag v-if="row.auditStatus === 20" type="success">瀹℃牳閫氳繃</el-tag>
+<el-tag v-if="row.auditStatus === 30" type="danger">瀹℃牳涓嶉�氳繃</el-tag>
+```
+
+### 3. 閿�鍞姤浠峰崟鍒楄〃椤甸潰
+
+**绉婚櫎瀹℃壒鐩稿叧鎿嶄綔鎸夐挳锛�**
+
+```html
+<!-- 绉婚櫎浠ヤ笅鎸夐挳 -->
+<!-- <el-button v-if="row.status === 0" type="success" size="small" @click="handleSubmit(row)">鎻愪氦瀹℃壒</el-button> -->
+
+<!-- 淇濈暀鎸夐挳 -->
+<el-button v-if="row.status === 0" type="primary" size="small" @click="handleUpdate(row)">缂栬緫</el-button>
+<el-button v-if="row.status === 0" type="warning" size="small" @click="handleConvertContract(row)">杞悎鍚�</el-button>
+<el-button v-if="row.status === 0" type="danger" size="small" @click="handleDelete(row)">鍒犻櫎</el-button>
+<el-button v-if="row.contractId" type="primary" size="small" @click="handleViewContract(row)">鏌ョ湅鍚堝悓</el-button>
+```
+
+### 4. 鎶ヤ环鍗曠姸鎬佹樉绀�
+
+```html
+<!-- 鐘舵�佹爣绛� -->
+<el-tag v-if="row.status === 0" type="info">鑽夌</el-tag>
+<el-tag v-if="row.status === 10" type="success">宸茶浆鍚堝悓</el-tag>
+```
+
+### 5. API 鏂囦欢淇敼
+
+```js
+// /src/api/crm/contract.js
+
+// 鏂板 API
+export function getContractApproveProcessList() {
+ return request({ url: '/crm/contract/approve-process-list', method: 'get' })
+}
+
+export function submitContract(id, processDefinitionKey) {
+ return request({ url: '/crm/contract/submit', method: 'put', params: { id, processDefinitionKey } })
+}
+
+// /src/api/crm/saleQuotation.js
+
+// 绉婚櫎浠ヤ笅 API
+// export function submitSaleQuotation(id, processDefinitionKey) { ... }
+// export function getSaleQuotationApproveProcessList() { ... }
+
+// 淇濈暀浠ヤ笅 API
+export function createSaleQuotation(data) { ... }
+export function updateSaleQuotation(data) { ... }
+export function deleteSaleQuotation(ids) { ... }
+export function getSaleQuotation(id) { ... }
+export function getSaleQuotationPage(params) { ... }
+export function convertSaleQuotationToContract(id) { ... }
+```
+
+---
+
+## 涓氬姟娴佺▼鍙樻洿
+
+### 鏂版祦绋�
+
+```
+鎶ヤ环鍗�(鑽夌) 鈫� 杞悎鍚� 鈫� 鍚堝悓(鑽夌) 鈫� 鐢ㄦ埛閫夋嫨瀹℃壒娴佺▼ 鈫� 鎻愪氦瀹℃壒 鈫� BPM瀹℃壒 鈫� 瀹℃壒閫氳繃 鈫� 鐢熸垚璁㈠崟
+```
+
+**娴佺▼璇存槑锛�**
+1. 閿�鍞姤浠蜂笉鍐嶉渶瑕佸鎵癸紝鑽夌鐘舵�佸彲鐩存帴杞悎鍚�
+2. 鍚堝悓鍒涘缓鍚庝负鑽夌鐘舵�侊紝闇�鐢ㄦ埛鎵嬪姩閫夋嫨瀹℃壒娴佺▼骞舵彁浜�
+3. 鐐瑰嚮"鎻愪氦瀹℃壒"鎸夐挳鍚庯紝寮瑰嚭娴佺▼閫夋嫨妗嗭紝鐢ㄦ埛閫夋嫨娴佺▼鍚庢彁浜�
+
+---
+
+## 娉ㄦ剰浜嬮」
+
+1. **鍚堝悓瀹℃壒 BPM 鍒嗙被**锛氶渶瑕佸湪 BPM 绠$悊鍚庡彴鍒涘缓鍒嗙被缂栫爜涓� `contract_approve` 鐨勫垎绫�
+2. **鍚堝悓瀹℃壒娴佺▼**锛氶渶瑕佸湪璇ュ垎绫讳笅閰嶇疆鑷冲皯涓�涓縺娲荤殑瀹℃壒娴佺▼
+3. **娴佺▼閫夋嫨**锛氬鏋滃彧鏈変竴涓祦绋嬶紝鍓嶇鍙互鐩存帴鎻愪氦锛涘鏋滄湁澶氫釜娴佺▼锛岄渶瑕佺敤鎴烽�夋嫨
+4. **杞悎鍚屽悗**锛氭姤浠疯浆鍚堝悓鍚庯紝鍚堝悓涓鸿崏绋跨姸鎬侊紝闇�鎵嬪姩鎻愪氦瀹℃壒
\ No newline at end of file
diff --git a/docs/contract_product_field_change.md b/docs/contract_product_field_change.md
new file mode 100644
index 0000000..f16c12a
--- /dev/null
+++ b/docs/contract_product_field_change.md
@@ -0,0 +1,211 @@
+# 鍚堝悓妯″潡浜у搧瀛楁鍙樻洿
+
+## 鍙樻洿璇存槑
+
+鍚堝悓妯″潡鐨勪骇鍝佹潵婧愪粠 **CRM 浜у搧** 鏀逛负 **MDM 鐗╂枡**锛屾秹鍙婂瓧娈靛悕绉板彉鏇淬��
+
+---
+
+## 鏁版嵁搴撳彉鏇� SQL
+
+```sql
+-- =============================================
+-- 鍚堝悓浜у搧琛ㄥ瓧娈靛彉鏇�
+-- =============================================
+
+-- 1. 鍒犻櫎鏃у瓧娈碉紙濡傛灉鏈夋暟鎹鍏堝浠斤級
+ALTER TABLE `crm_contract_product` DROP COLUMN `product_id`;
+ALTER TABLE `crm_contract_product` DROP COLUMN `product_price`;
+
+-- 2. 濡傛灉鏂板瓧娈典笉瀛樺湪锛屽垯娣诲姞锛堟牴鎹疄闄呮儏鍐靛垽鏂槸鍚﹂渶瑕佹墽琛岋級
+-- ALTER TABLE `crm_contract_product` ADD COLUMN `item_id` bigint DEFAULT NULL COMMENT 'MDM鐗╂枡缂栧彿' AFTER `contract_id`;
+-- ALTER TABLE `crm_contract_product` ADD COLUMN `item_unit_id` bigint DEFAULT NULL COMMENT '璁¢噺鍗曚綅缂栧彿' AFTER `item_id`;
+-- ALTER TABLE `crm_contract_product` ADD COLUMN `item_price` decimal(24,6) DEFAULT NULL COMMENT '鐗╂枡鍘熶环' AFTER `item_unit_id`;
+```
+
+---
+
+## 娑夊強椤甸潰
+
+- 鍚堝悓琛ㄥ崟椤甸潰锛堟柊寤�/缂栬緫鍚堝悓锛�
+- 鍚堝悓璇︽儏椤甸潰
+
+---
+
+## API 鍙樻洿
+
+### 1. 鍒涘缓/鏇存柊鍚堝悓鎺ュ彛
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | /crm/contract/create | 鍒涘缓鍚堝悓 |
+| PUT | /crm/contract/update | 鏇存柊鍚堝悓 |
+
+### 2. 璇锋眰鍙傛暟鍙樻洿锛坧roducts 瀛楁锛�
+
+#### 鏃у瓧娈碉紙宸插簾寮冿級
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| productId | Long | 鏄� | CRM 浜у搧缂栧彿 |
+| productPrice | BigDecimal | 鍚� | 浜у搧浠锋牸 |
+| sellingPrice | BigDecimal | 鍚� | 閿�鍞环锛堢敤浣滃悎鍚屼环锛� |
+
+#### 鏂板瓧娈�
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| itemId | Long | 鏄� | MDM 鐗╂枡缂栧彿 |
+| itemPrice | BigDecimal | 鏄� | 鐗╂枡鍘熶环 |
+| contractPrice | BigDecimal | 鏄� | 鍚堝悓浠锋牸 |
+| count | Integer | 鏄� | 鏁伴噺 |
+
+### 3. 璇锋眰绀轰緥
+
+**鏃ф牸寮忥紙涓嶅啀鏀寔锛夛細**
+
+```json
+{
+ "name": "鍚堝悓鍚嶇О",
+ "customerId": 1,
+ "products": [
+ {
+ "productId": 4,
+ "sellingPrice": 1111,
+ "count": 10
+ }
+ ]
+}
+```
+
+**鏂版牸寮忥細**
+
+```json
+{
+ "name": "鍚堝悓鍚嶇О",
+ "customerId": 1,
+ "products": [
+ {
+ "itemId": 4,
+ "itemPrice": 1000,
+ "contractPrice": 1111,
+ "count": 10
+ }
+ ]
+}
+```
+
+---
+
+## 鍝嶅簲鍙樻洿
+
+### 鍚堝悓璇︽儏鎺ュ彛鍝嶅簲
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | /crm/contract/get | 鑾峰彇鍚堝悓璇︽儏 |
+
+#### 浜у搧鏄庣粏瀛楁鍙樻洿
+
+| 鏃у瓧娈� | 鏂板瓧娈� | 璇存槑 |
+|--------|--------|------|
+| productId | itemId | MDM 鐗╂枡缂栧彿 |
+| productName | itemName | 鐗╂枡鍚嶇О |
+| productNo | itemCode | 鐗╂枡缂栫爜 |
+| productUnit | itemUnitName | 璁¢噺鍗曚綅鍚嶇О |
+| productPrice | itemPrice | 鐗╂枡鍘熶环 |
+| - | itemBarCode | 鐗╂枡鏉$爜锛堟柊澧烇級 |
+
+---
+
+## 鍓嶇淇敼鐐�
+
+### 1. 鐗╂枡閫夋嫨缁勪欢
+
+灏� **CRM 浜у搧閫夋嫨鍣�** 鏀逛负 **MDM 鐗╂枡閫夋嫨鍣�**銆�
+
+```html
+<!-- 鏃э細CRM 浜у搧閫夋嫨鍣� -->
+<CrmProductSelect v-model="product.productId" />
+
+<!-- 鏂帮細MDM 鐗╂枡閫夋嫨鍣� -->
+<MdmItemSelect v-model="product.itemId" @change="handleItemChange" />
+```
+
+### 2. 鐗╂枡閫夋嫨鍥炶皟
+
+```js
+methods: {
+ handleItemChange(item) {
+ // 閫夋嫨鐗╂枡鍚庤嚜鍔ㄥ~鍏呬环鏍间俊鎭�
+ this.product.itemId = item.id
+ this.product.itemPrice = item.salesPrice // 鐗╂枡鍘熶环
+ this.product.contractPrice = item.salesPrice // 鍚堝悓浠锋牸锛堥粯璁ょ瓑浜庡師浠凤紝鍙慨鏀癸級
+ }
+}
+```
+
+### 3. 琛ㄦ牸鍒楀瓧娈典慨鏀�
+
+```html
+<!-- 浜у搧鏄庣粏琛ㄦ牸 -->
+<el-table-column prop="itemCode" label="鐗╂枡缂栫爜" />
+<el-table-column prop="itemName" label="鐗╂枡鍚嶇О" />
+<el-table-column prop="itemBarCode" label="鐗╂枡鏉$爜" />
+<el-table-column prop="itemUnitName" label="鍗曚綅" />
+<el-table-column prop="itemPrice" label="鍘熶环" />
+<el-table-column prop="contractPrice" label="鍚堝悓浠�" />
+<el-table-column prop="count" label="鏁伴噺" />
+```
+
+### 4. data 鏁版嵁淇敼
+
+```js
+// 浜у搧鏄庣粏鏁版嵁缁撴瀯
+product: {
+ itemId: null, // MDM 鐗╂枡缂栧彿
+ itemPrice: null, // 鐗╂枡鍘熶环
+ contractPrice: null, // 鍚堝悓浠锋牸
+ count: null // 鏁伴噺
+}
+```
+
+---
+
+## MDM 鐗╂枡閫夋嫨鍣� API
+
+### 鑾峰彇鐗╂枡鍒楄〃
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | /mdm/item/page | 鍒嗛〉鏌ヨ鐗╂枡 |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| code | String | 鍚� | 鐗╂枡缂栫爜 |
+| name | String | 鍚� | 鐗╂枡鍚嶇О |
+| barCode | String | 鍚� | 鐗╂枡鏉$爜 |
+| status | Integer | 鍚� | 鐘舵�侊紙0鍚敤 1绂佺敤锛夛紝浼� 0 |
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| id | Long | 鐗╂枡缂栧彿锛堝嵆 itemId锛� |
+| code | String | 鐗╂枡缂栫爜 |
+| name | String | 鐗╂枡鍚嶇О |
+| barCode | String | 鐗╂枡鏉$爜 |
+| salesPrice | BigDecimal | 閿�鍞环 |
+| unitMeasureId | Long | 璁¢噺鍗曚綅缂栧彿 |
+| unitMeasureName | String | 璁¢噺鍗曚綅鍚嶇О |
+
+---
+
+## 娉ㄦ剰浜嬮」
+
+1. **鐗╂枡蹇呴』鍏堝惎鐢�**锛氬彧鑳介�夋嫨鐘舵�佷负"鍚敤"鐨� MDM 鐗╂枡
+2. **浠锋牸鍙慨鏀�**锛歚contractPrice` 榛樿绛変簬鐗╂枡鍘熶环锛屼絾鍙互鎵嬪姩淇敼
+3. **杩囨浮鏈�**锛氬悗绔殏鏃跺吋瀹� `productId` 鍜� `sellingPrice` 瀛楁锛屼絾鍚庣画浼氱Щ闄ゅ吋瀹归�昏緫
+4. **鍏朵粬妯″潡鍚屾淇敼**锛氶攢鍞姤浠峰崟涔熶娇鐢� MDM 鐗╂枡锛屽瓧娈典笌鍚堝悓涓�鑷�
diff --git a/docs/crm_erp_integration_frontend.md b/docs/crm_erp_integration_frontend.md
index 3a3afbc..9bf5edb 100644
--- a/docs/crm_erp_integration_frontend.md
+++ b/docs/crm_erp_integration_frontend.md
@@ -698,6 +698,10 @@
ALTER TABLE `crm_contract`
ADD COLUMN `order_id` bigint DEFAULT NULL COMMENT '鍏宠仈 ERP 閿�鍞鍗曠紪鍙�' AFTER `total_price`,
ADD COLUMN `order_no` varchar(32) DEFAULT NULL COMMENT 'ERP 閿�鍞鍗曞崟鍙�' AFTER `order_id`;
+
+-- 鏂板瀹氶噾閲戦瀛楁
+ALTER TABLE `crm_contract`
+ADD COLUMN `deposit_price` decimal(24,6) DEFAULT NULL COMMENT '瀹氶噾閲戦锛屽崟浣嶏細鍏�' AFTER `total_price`;
```
### 5.2 CRM 鍟嗘満琛�
@@ -1006,3 +1010,198 @@
|------|------|
| `docs/crm_erp_integration_frontend.md` | 鏈枃妗� |
| `docs/crm_erp_integration_sql.sql` | SQL 鍙樻洿鑴氭湰 |
+
+---
+
+## 鍗佷簩銆侀攢鍞鍗曚綔搴熷姛鑳斤紙鏂板锛�
+
+### 12.1 鍔熻兘璇存槑
+
+閿�鍞鍗曚綔搴熷姛鑳界敤浜庢爣璁板凡瀹℃牳鐨勮鍗曚负"宸蹭綔搴�"鐘舵�侊紝瀹炵幇璁㈠崟鍏ㄦ祦绋嬪彲杩芥函銆�
+
+**涓氬姟瑙勫垯锛�**
+- 鍙湁宸插鏍哥殑璁㈠崟鎵嶈兘浣滃簾
+- 宸插嚭搴撴垨宸查��璐х殑璁㈠崟涓嶈兘浣滃簾
+- 浣滃簾鍚庤鍗曠姸鎬佸彉涓�"宸蹭綔搴�"
+- 浣滃簾鍚庡悓姝ユ洿鏂板叧鑱斿悎鍚岀姸鎬佷负"宸蹭綔搴�"
+- 绠$悊鍛樺彲浠ラ噸鏂板惎鐢ㄥ凡浣滃簾鐨勮鍗�
+
+### 12.2 鐘舵�佹灇涓�
+
+| 鐘舵�佸�� | 鏋氫妇 | 璇存槑 |
+|--------|------|------|
+| 10 | PROCESS | 鏈鏍� |
+| 20 | APPROVE | 宸插鏍� |
+| 30 | CANCEL | 宸蹭綔搴� |
+
+### 12.3 鍚堝悓鐘舵�佸悓姝�
+
+褰撻攢鍞鍗曚綔搴�/鍚敤鏃讹紝浼氳嚜鍔ㄥ悓姝ュ叧鑱斿悎鍚岀殑鐘舵�侊細
+
+| 鎿嶄綔 | 璁㈠崟鐘舵�佸彉鍖� | 鍚堝悓鐘舵�佸彉鍖� |
+|------|--------------|--------------|
+| 浣滃簾璁㈠崟 | 宸插鏍� 鈫� 宸蹭綔搴� | 瀹℃牳閫氳繃 鈫� 宸蹭綔搴� |
+| 鍚敤璁㈠崟 | 宸蹭綔搴� 鈫� 宸插鏍� | 宸蹭綔搴� 鈫� 瀹℃牳閫氳繃 |
+
+**鍚堝悓鐘舵�佹灇涓撅細**
+
+| 鐘舵�佸�� | 鏋氫妇 | 璇存槑 |
+|--------|------|------|
+| 0 | DRAFT | 鏈彁浜� |
+| 10 | PROCESS | 瀹℃壒涓� |
+| 20 | APPROVE | 瀹℃牳閫氳繃 |
+| 30 | REJECT | 瀹℃牳涓嶉�氳繃 |
+| 40 | CANCEL | 宸插彇娑� |
+| 50 | INVALID | 宸蹭綔搴� |
+
+### 12.4 API 鎺ュ彛
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| PUT | /erp/sale-order/cancel | 浣滃簾閿�鍞鍗� |
+| PUT | /erp/sale-order/enable | 閲嶆柊鍚敤閿�鍞鍗� |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| id | Long | 鏄� | 閿�鍞鍗曠紪鍙� |
+
+**鍝嶅簲锛�**
+
+```json
+{
+ "code": 0,
+ "msg": "鎿嶄綔鎴愬姛"
+}
+```
+
+**閿欒鐮侊細**
+
+| 閿欒鐮� | 璇存槑 |
+|--------|------|
+| 1_020_201_011 | 浣滃簾澶辫触锛岃鍗曟湭瀹℃牳 |
+| 1_020_201_012 | 浣滃簾澶辫触锛屽凡瀛樺湪瀵瑰簲鐨勯攢鍞嚭搴撳崟 |
+| 1_020_201_013 | 浣滃簾澶辫触锛屽凡瀛樺湪瀵瑰簲鐨勯攢鍞��璐у崟 |
+| 1_020_201_014 | 浣滃簾澶辫触锛岃鍒锋柊椤甸潰鍚庨噸璇� |
+| 1_020_201_015 | 鍚敤澶辫触锛岃鍗曟湭浣滃簾 |
+| 1_020_201_016 | 鍚敤澶辫触锛岃鍒锋柊椤甸潰鍚庨噸璇� |
+
+### 12.5 鍓嶇淇敼鐐�
+
+**鏂囦欢璺緞锛�** `/src/views/erp/sale/order/index.vue`
+
+**鎿嶄綔鍒楁寜閽細**
+
+```html
+<el-table-column label="鎿嶄綔" width="280">
+ <template #default="{ row }">
+ <el-button v-if="row.status === 10" type="primary" size="small" @click="handleUpdate(row)">缂栬緫</el-button>
+ <el-button v-if="row.status === 10" type="success" size="small" @click="handleApprove(row)">瀹℃牳</el-button>
+ <el-button v-if="row.status === 20" type="warning" size="small" @click="handleProcess(row)">鍙嶅鏍�</el-button>
+ <el-button v-if="row.status === 20" type="danger" size="small" @click="handleCancel(row)">浣滃簾</el-button>
+ <el-button v-if="row.status === 30" type="success" size="small" @click="handleEnable(row)">鍚敤</el-button>
+ <el-button v-if="row.status === 10" type="danger" size="small" @click="handleDelete(row)">鍒犻櫎</el-button>
+ </template>
+</el-table-column>
+```
+
+**鐘舵�佹爣绛撅細**
+
+```html
+<el-table-column label="鐘舵��" prop="status" width="100">
+ <template #default="{ row }">
+ <el-tag v-if="row.status === 10" type="warning">鏈鏍�</el-tag>
+ <el-tag v-if="row.status === 20" type="success">宸插鏍�</el-tag>
+ <el-tag v-if="row.status === 30" type="info">宸蹭綔搴�</el-tag>
+ </template>
+</el-table-column>
+```
+
+**鍏宠仈鍚堝悓鍒楋紙浣滃簾鏃舵樉绀虹孩鑹诧級锛�**
+
+```html
+<el-table-column label="鍏宠仈鍚堝悓" prop="contractNo" width="150">
+ <template #default="{ row }">
+ <el-link
+ v-if="row.contractId"
+ :type="row.status === 30 ? 'danger' : 'primary'"
+ @click="handleViewContract(row)"
+ >
+ {{ row.contractNo }}
+ </el-link>
+ <span v-else>-</span>
+ </template>
+</el-table-column>
+```
+
+**JS 鏂规硶锛�**
+
+```js
+import { cancelSaleOrder, enableSaleOrder } from '@/api/erp/saleOrder'
+
+methods: {
+ async handleCancel(row) {
+ try {
+ await this.$confirm('纭浣滃簾璇ラ攢鍞鍗曪紵浣滃簾鍚庡叧鑱斿悎鍚屼篃灏嗘爣璁颁负宸蹭綔搴燂紒', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'warning'
+ })
+ await cancelSaleOrder(row.id)
+ this.$message.success('浣滃簾鎴愬姛')
+ this.getList()
+ } catch (e) {
+ if (e !== 'cancel') {
+ this.$message.error(e.message || '浣滃簾澶辫触')
+ }
+ }
+ },
+ async handleEnable(row) {
+ try {
+ await this.$confirm('纭閲嶆柊鍚敤璇ラ攢鍞鍗曪紵鍚敤鍚庡叧鑱斿悎鍚屽皢鎭㈠涓哄鏍搁�氳繃鐘舵�侊紒', '鎻愮ず', {
+ confirmButtonText: '纭畾',
+ cancelButtonText: '鍙栨秷',
+ type: 'info'
+ })
+ await enableSaleOrder(row.id)
+ this.$message.success('鍚敤鎴愬姛')
+ this.getList()
+ } catch (e) {
+ if (e !== 'cancel') {
+ this.$message.error(e.message || '鍚敤澶辫触')
+ }
+ }
+ }
+}
+```
+
+**API 瀹氫箟鏂囦欢锛�** `/src/api/erp/saleOrder.js`
+
+```js
+// 浣滃簾閿�鍞鍗�
+export function cancelSaleOrder(id) {
+ return request({
+ url: '/erp/sale-order/cancel',
+ method: 'put',
+ params: { id }
+ })
+}
+
+// 閲嶆柊鍚敤閿�鍞鍗�
+export function enableSaleOrder(id) {
+ return request({
+ url: '/erp/sale-order/enable',
+ method: 'put',
+ params: { id }
+ })
+}
+```
+
+### 12.6 娉ㄦ剰浜嬮」
+
+1. 浣滃簾鎿嶄綔浼氬悓姝ユ洿鏂板叧鑱斿悎鍚岀姸鎬佷负"宸蹭綔搴�"
+2. 鍚敤鎿嶄綔浼氬悓姝ユ洿鏂板叧鑱斿悎鍚岀姸鎬佷负"瀹℃牳閫氳繃"
+3. 宸插嚭搴�/宸查��璐х殑璁㈠崟涓嶈兘浣滃簾锛岄渶鍏堝鐞嗙浉鍏充笟鍔″崟鎹�
+4. 浣滃簾鍚庤鍗曞彿鏄剧ず涓虹孩鑹诧紝鎻愰啋鐢ㄦ埛璇ヨ鍗曞凡澶辨晥
+5. 鍚敤鍔熻兘闇�瑕佺鐞嗗憳鏉冮檺锛坄erp:sale-order:enable`锛�
diff --git a/sql/mysql/mes_auto_code_rule_init.sql b/sql/mysql/mes_auto_code_rule_init.sql
new file mode 100644
index 0000000..0fe4242
--- /dev/null
+++ b/sql/mysql/mes_auto_code_rule_init.sql
@@ -0,0 +1,523 @@
+-- =============================================
+-- MES 缂栫爜瑙勫垯鍒濆鍖栬剼鏈�
+-- 鎵ц鍓嶈纭繚 mes_md_auto_code_rule 鍜� mes_md_auto_code_part 琛ㄥ凡瀛樺湪
+-- =============================================
+
+-- 娓呯┖鐜版湁鏁版嵁锛堥噸鏂板垵濮嬪寲鏃舵墽琛岋級
+DELETE FROM mes_md_auto_code_part;
+DELETE FROM mes_md_auto_code_rule;
+
+-- =============================================
+-- 1. 鍩虹涓绘暟鎹紪鐮佽鍒�
+-- =============================================
+
+-- 1.1 鐗╂枡鍒嗙被缂栫爜瑙勫垯 (MD_ITEM_TYPE_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (1, 'MD_ITEM_TYPE_CODE', '鐗╂枡鍒嗙被缂栫爜', '鐢ㄤ簬鐢熸垚鐗╂枡鍒嗙被缂栫爜', 10, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (1, 1, 3, 3, 'ITC', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (1, 2, 4, 4, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 1.2 鐗╂枡缂栫爜瑙勫垯 (MD_ITEM_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (2, 'MD_ITEM_CODE', '鐗╂枡缂栫爜', '鐢ㄤ簬鐢熸垚鐗╂枡缂栫爜', 20, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (2, 1, 3, 5, 'ITEM_', NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (2, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (2, 3, 4, 4, NULL, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 1.3 渚涘簲鍟嗙紪鐮佽鍒� (MD_VENDOR_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (3, 'MD_VENDOR_CODE', '渚涘簲鍟嗙紪鐮�', '鐢ㄤ簬鐢熸垚渚涘簲鍟嗙紪鐮�', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (3, 1, 3, 3, 'VDR', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (3, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 1.4 瀹㈡埛缂栫爜瑙勫垯 (MD_CLIENT_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (4, 'MD_CLIENT_CODE', '瀹㈡埛缂栫爜', '鐢ㄤ簬鐢熸垚瀹㈡埛缂栫爜', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (4, 1, 3, 3, 'CLT', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (4, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 1.5 宸ヤ綔绔欑紪鐮佽鍒� (MD_WORKSTATION_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (5, 'MD_WORKSTATION_CODE', '宸ヤ綔绔欑紪鐮�', '鐢ㄤ簬鐢熸垚宸ヤ綔绔欑紪鐮�', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (5, 1, 3, 3, 'WKS', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (5, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 1.6 杞﹂棿缂栫爜瑙勫垯 (MD_WORKSHOP_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (6, 'MD_WORKSHOP_CODE', '杞﹂棿缂栫爜', '鐢ㄤ簬鐢熸垚杞﹂棿缂栫爜', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (6, 1, 3, 3, 'WSP', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (6, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 1.7 宸ュ叿绫诲瀷缂栫爜瑙勫垯 (TM_TOOL_TYPE_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (7, 'TM_TOOL_TYPE_CODE', '宸ュ叿绫诲瀷缂栫爜', '鐢ㄤ簬鐢熸垚宸ュ叿绫诲瀷缂栫爜', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (7, 1, 3, 3, 'TTL', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (7, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- =============================================
+-- 2. 浠撳簱绠$悊缂栫爜瑙勫垯
+-- =============================================
+
+-- 2.1 浠撳簱缂栫爜 (WM_WAREHOUSE_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (8, 'WM_WAREHOUSE_CODE', '浠撳簱缂栫爜', '鐢ㄤ簬鐢熸垚浠撳簱缂栫爜', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (8, 1, 3, 3, 'WHS', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (8, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.2 搴撳尯缂栫爜 (WM_LOCATION_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (9, 'WM_LOCATION_CODE', '搴撳尯缂栫爜', '鐢ㄤ簬鐢熸垚搴撳尯缂栫爜', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (9, 1, 3, 3, 'LOC', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (9, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.3 搴撲綅缂栫爜 (WM_AREA_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (10, 'WM_AREA_CODE', '搴撲綅缂栫爜', '鐢ㄤ簬鐢熸垚搴撲綅缂栫爜', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (10, 1, 3, 3, 'ARA', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (10, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.4 鎵规缂栫爜 (WM_BATCH_CODE) - 鎸夊ぉ寰幆
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (11, 'WM_BATCH_CODE', '鎵规缂栫爜', '鐢ㄤ簬鐢熸垚鎵规缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (11, 1, 3, 1, 'B', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (11, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (11, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.5 SN鐮� (WM_SN_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (12, 'WM_SN_CODE', 'SN鐮�', '鐢ㄤ簬鐢熸垚SN鐮�', 20, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (12, 1, 3, 3, 'SN_', NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (12, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (12, 3, 4, 6, NULL, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.6 瑁呯鍗曠紪鐮� (WM_PACKAGE_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (13, 'WM_PACKAGE_CODE', '瑁呯鍗曠紪鐮�', '鐢ㄤ簬鐢熸垚瑁呯鍗曠紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, remark, creator, create_time, updater, update_time, deleted)
+VALUES (13, 1, 3, 4, 'PKG_', NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, remark, creator, create_time, updater, update_time, deleted)
+VALUES (13, 2, 2, 8, NULL, 'yyyyMMdd', '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.7 鍒拌揣閫氱煡鍗曠紪鐮� (WM_ARRIVAL_NOTICE_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (14, 'WM_ARRIVAL_NOTICE_CODE', '鍒拌揣閫氱煡鍗曠紪鐮�', '鐢ㄤ簬鐢熸垚鍒拌揣閫氱煡鍗曠紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (14, 1, 3, 3, 'AN_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (14, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (14, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.8 閲囪喘鍏ュ簱鍗曠紪鐮� (WM_ITEM_RECEIPT_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (15, 'WM_ITEM_RECEIPT_CODE', '閲囪喘鍏ュ簱鍗曠紪鐮�', '鐢ㄤ簬鐢熸垚閲囪喘鍏ュ簱鍗曠紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (15, 1, 3, 3, 'IR_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (15, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (15, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.9 閲囪喘閫�璐у崟缂栫爜 (WM_RETURN_VENDOR_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (16, 'WM_RETURN_VENDOR_CODE', '閲囪喘閫�璐у崟缂栫爜', '鐢ㄤ簬鐢熸垚閲囪喘閫�璐у崟缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (16, 1, 3, 3, 'RV_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (16, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (16, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.10 鐢熶骇閫�鏂欏崟缂栫爜 (WM_RETURN_ISSUE_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (17, 'WM_RETURN_ISSUE_CODE', '鐢熶骇閫�鏂欏崟缂栫爜', '鐢ㄤ簬鐢熸垚鐢熶骇閫�鏂欏崟缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (17, 1, 3, 3, 'RI_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (17, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (17, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.11 閿�鍞嚭搴撳崟缂栫爜 (WM_PRODUCT_SALES_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (18, 'WM_PRODUCT_SALES_CODE', '閿�鍞嚭搴撳崟缂栫爜', '鐢ㄤ簬鐢熸垚閿�鍞嚭搴撳崟缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (18, 1, 3, 3, 'SO_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (18, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (18, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.12 鍙戣揣閫氱煡鍗曠紪鐮� (WM_SALES_NOTICE_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (19, 'WM_SALES_NOTICE_CODE', '鍙戣揣閫氱煡鍗曠紪鐮�', '鐢ㄤ簬鐢熸垚鍙戣揣閫氱煡鍗曠紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (19, 1, 3, 3, 'SN_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (19, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (19, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.13 閿�鍞��璐у崟缂栫爜 (WM_RETURN_SALES_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (20, 'WM_RETURN_SALES_CODE', '閿�鍞��璐у崟缂栫爜', '鐢ㄤ簬鐢熸垚閿�鍞��璐у崟缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (20, 1, 3, 3, 'RS_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (20, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (20, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.14 鏉傞」鍏ュ簱鍗曠紪鐮� (WM_MISC_RECEIPT_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (21, 'WM_MISC_RECEIPT_CODE', '鏉傞」鍏ュ簱鍗曠紪鐮�', '鐢ㄤ簬鐢熸垚鏉傞」鍏ュ簱鍗曠紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (21, 1, 3, 3, 'MR_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (21, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (21, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.15 鏉傞」鍑哄簱鍗曠紪鐮� (WM_MISC_ISSUE_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (22, 'WM_MISC_ISSUE_CODE', '鏉傞」鍑哄簱鍗曠紪鐮�', '鐢ㄤ簬鐢熸垚鏉傞」鍑哄簱鍗曠紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (22, 1, 3, 3, 'MI_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (22, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (22, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.16 鐩樼偣鏂规缂栫爜 (WM_STOCK_TAKING_PLAN_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (23, 'WM_STOCK_TAKING_PLAN_CODE', '鐩樼偣鏂规缂栫爜', '鐢ㄤ簬鐢熸垚鐩樼偣鏂规缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (23, 1, 3, 3, 'SP_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (23, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (23, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.17 鐩樼偣浠诲姟缂栫爜 (WM_STOCK_TAKING_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (24, 'WM_STOCK_TAKING_CODE', '鐩樼偣浠诲姟缂栫爜', '鐢ㄤ簬鐢熸垚鐩樼偣浠诲姟缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (24, 1, 3, 3, 'ST_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (24, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (24, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.18 杞Щ璋冩嫧鍗曠紪鐮� (TRANSFER_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (25, 'TRANSFER_CODE', '杞Щ璋冩嫧鍗曠紪鐮�', '鐢ㄤ簬鐢熸垚杞Щ璋冩嫧鍗曠紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (25, 1, 3, 3, 'TR_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (25, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (25, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.19 澶栧崗鍙戞枡鍗曠紪鐮� (WM_OUTSOURCE_ISSUE_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (26, 'WM_OUTSOURCE_ISSUE_CODE', '澶栧崗鍙戞枡鍗曠紪鐮�', '鐢ㄤ簬鐢熸垚澶栧崗鍙戞枡鍗曠紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (26, 1, 3, 3, 'OI_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (26, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (26, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 2.20 澶栧崗鍏ュ簱鍗曠紪鐮� (WM_OUTSOURCE_RECEIPT_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (27, 'WM_OUTSOURCE_RECEIPT_CODE', '澶栧崗鍏ュ簱鍗曠紪鐮�', '鐢ㄤ簬鐢熸垚澶栧崗鍏ュ簱鍗曠紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (27, 1, 3, 3, 'OR_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (27, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (27, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- =============================================
+-- 3. 鐢熶骇绠$悊缂栫爜瑙勫垯
+-- =============================================
+
+-- 3.1 鐢熶骇宸ュ崟缂栫爜 (PRO_WORK_ORDER_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (28, 'PRO_WORK_ORDER_CODE', '鐢熶骇宸ュ崟缂栫爜', '鐢ㄤ簬鐢熸垚鐢熶骇宸ュ崟缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (28, 1, 3, 3, 'WO_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (28, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (28, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 3.2 鐢熶骇浠诲姟缂栫爜 (PRO_TASK_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (29, 'PRO_TASK_CODE', '鐢熶骇浠诲姟缂栫爜', '鐢ㄤ簬鐢熸垚鐢熶骇浠诲姟缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (29, 1, 3, 3, 'TK_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (29, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (29, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 3.3 鐢熶骇鎶ュ伐鍗曠紪鐮� (PRO_FEEDBACK_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (30, 'PRO_FEEDBACK_CODE', '鐢熶骇鎶ュ伐鍗曠紪鐮�', '鐢ㄤ簬鐢熸垚鐢熶骇鎶ュ伐鍗曠紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (30, 1, 3, 3, 'FB_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (30, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (30, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 3.4 宸ヨ壓璺嚎缂栫爜 (PRO_ROUTE_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (31, 'PRO_ROUTE_CODE', '宸ヨ壓璺嚎缂栫爜', '鐢ㄤ簬鐢熸垚宸ヨ壓璺嚎缂栫爜', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (31, 1, 3, 3, 'RT_', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (31, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- =============================================
+-- 4. 璐ㄩ噺绠$悊缂栫爜瑙勫垯
+-- =============================================
+
+-- 4.1 鏉ユ枡妫�楠屽崟缂栫爜 (QC_IQC_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (32, 'QC_IQC_CODE', '鏉ユ枡妫�楠屽崟缂栫爜', '鐢ㄤ簬鐢熸垚鏉ユ枡妫�楠屽崟缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (32, 1, 3, 4, 'IQC_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (32, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (32, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 4.2 杩囩▼妫�楠屽崟缂栫爜 (QC_IPQC_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (33, 'QC_IPQC_CODE', '杩囩▼妫�楠屽崟缂栫爜', '鐢ㄤ簬鐢熸垚杩囩▼妫�楠屽崟缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (33, 1, 3, 5, 'IPQC_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (33, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (33, 3, 4, 3, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 4.3 鍑鸿揣妫�楠屽崟缂栫爜 (QC_OQC_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (34, 'QC_OQC_CODE', '鍑鸿揣妫�楠屽崟缂栫爜', '鐢ㄤ簬鐢熸垚鍑鸿揣妫�楠屽崟缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (34, 1, 3, 5, 'OQC_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (34, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (34, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 4.4 閫�璐ф楠屽崟缂栫爜 (QC_RQC_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (35, 'QC_RQC_CODE', '閫�璐ф楠屽崟缂栫爜', '鐢ㄤ簬鐢熸垚閫�璐ф楠屽崟缂栫爜', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (35, 1, 3, 4, 'RQC_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (35, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (35, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- 4.5 缂洪櫡绫诲瀷缂栫爜 (QC_DEFECT_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (36, 'QC_DEFECT_CODE', '缂洪櫡绫诲瀷缂栫爜', '鐢ㄤ簬鐢熸垚缂洪櫡绫诲瀷缂栫爜', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (36, 1, 3, 3, 'DFC', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (36, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 4.6 鏍峰搧妫�楠岀粨鏋滅紪鐮� (QC_INDICATOR_RESULT_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (37, 'QC_INDICATOR_RESULT_CODE', '鏍峰搧妫�楠岀粨鏋滅紪鐮�', '鐢ㄤ簬鐢熸垚鏍峰搧妫�楠岀粨鏋滅紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (37, 1, 3, 4, 'QIR_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (37, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (37, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- =============================================
+-- 5. 璁惧绠$悊缂栫爜瑙勫垯
+-- =============================================
+
+-- 5.1 璁惧缂栫爜 (DV_MACHINERY_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (38, 'DV_MACHINERY_CODE', '璁惧缂栫爜', '鐢ㄤ簬鐢熸垚璁惧缂栫爜', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (38, 1, 3, 3, 'MC_', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (38, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 5.2 璁惧绫诲瀷缂栫爜 (DV_MACHINERY_TYPE_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (39, 'DV_MACHINERY_TYPE_CODE', '璁惧绫诲瀷缂栫爜', '鐢ㄤ簬鐢熸垚璁惧绫诲瀷缂栫爜', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (39, 1, 3, 3, 'MCT', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (39, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 5.3 鐐规淇濆吇椤圭洰缂栫爜 (DV_SUBJECT_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (40, 'DV_SUBJECT_CODE', '鐐规淇濆吇椤圭洰缂栫爜', '鐢ㄤ簬鐢熸垚鐐规淇濆吇椤圭洰缂栫爜', 12, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (40, 1, 3, 3, 'SBJ', NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, serial_start_no, serial_step, cycle_flag, remark, creator, create_time, updater, update_time, deleted)
+VALUES (40, 2, 4, 6, NULL, 1, 1, b'0', '娴佹按鍙�', '1', NOW(), '1', NOW(), b'0');
+
+-- 5.4 缁翠慨鍗曠紪鐮� (DV_REPAIR_CODE)
+INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, status, creator, create_time, updater, update_time, deleted)
+VALUES (41, 'DV_REPAIR_CODE', '缁翠慨鍗曠紪鐮�', '鐢ㄤ簬鐢熸垚缁翠慨鍗曠紪鐮�', 16, b'0', 0, '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (41, 1, 3, 3, 'RP_', NULL, NULL, NULL, NULL, NULL, '鍥哄畾鍓嶇紑', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (41, 2, 2, 8, NULL, 'yyyyMMdd', NULL, NULL, NULL, NULL, '鏃ユ湡', '1', NOW(), '1', NOW(), b'0');
+
+INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, fix_character, date_format, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, create_time, updater, update_time, deleted)
+VALUES (41, 3, 4, 4, NULL, NULL, 1, 1, b'1', 3, '娴佹按鍙�-鎸夊ぉ寰幆', '1', NOW(), '1', NOW(), b'0');
+
+-- =============================================
+-- 鎵ц瀹屾垚鎻愮ず
+-- =============================================
+SELECT 'MES 缂栫爜瑙勫垯鍒濆鍖栧畬鎴愶紒' AS result;
+SELECT COUNT(*) AS rule_count FROM mes_md_auto_code_rule WHERE deleted = 0;
+SELECT COUNT(*) AS part_count FROM mes_md_auto_code_part WHERE deleted = 0;
\ No newline at end of file
diff --git a/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/contract/CrmContractApi.java b/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/contract/CrmContractApi.java
new file mode 100644
index 0000000..172864f
--- /dev/null
+++ b/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/contract/CrmContractApi.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.crm.api.contract;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+
+/**
+ * CRM 鍚堝悓 API 鎺ュ彛
+ *
+ * @author 鑺嬮亾婧愮爜
+ */
+public interface CrmContractApi {
+
+ String PREFIX = "/rpc-api/crm/contract";
+
+ /**
+ * 鏇存柊鍚堝悓鐘舵��
+ *
+ * @param id 鍚堝悓缂栧彿
+ * @param status 鐘舵��
+ */
+ CommonResult<Boolean> updateContractStatus(Long id, Integer status);
+
+ /**
+ * 鏍规嵁閿�鍞鍗曠紪鍙锋洿鏂板悎鍚岀姸鎬�
+ *
+ * @param orderId 閿�鍞鍗曠紪鍙�
+ * @param status 鐘舵��
+ */
+ CommonResult<Boolean> updateContractStatusByOrderId(Long orderId, Integer status);
+
+}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/contract/CrmContractApiImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/contract/CrmContractApiImpl.java
new file mode 100644
index 0000000..26bf5e4
--- /dev/null
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/contract/CrmContractApiImpl.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.crm.api.contract;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
+import jakarta.annotation.Resource;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+/**
+ * CRM 鍚堝悓 API 瀹炵幇绫�
+ *
+ * @author 鑺嬮亾婧愮爜
+ */
+@RestController
+@RequestMapping(CrmContractApi.PREFIX)
+@Validated
+public class CrmContractApiImpl implements CrmContractApi {
+
+ @Resource
+ @Lazy // 寤惰繜鍔犺浇锛岄伩鍏嶅惊鐜緷璧�
+ private CrmContractService contractService;
+
+ @Override
+ public CommonResult<Boolean> updateContractStatus(Long id, Integer status) {
+ contractService.updateContractStatus(id, status);
+ return success(true);
+ }
+
+ @Override
+ public CommonResult<Boolean> updateContractStatusByOrderId(Long orderId, Integer status) {
+ contractService.updateContractStatusByOrderId(orderId, status);
+ return success(true);
+ }
+
+}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/report/CrmReportApiImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/report/CrmReportApiImpl.java
index 379c959..1c6d15e 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/report/CrmReportApiImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/report/CrmReportApiImpl.java
@@ -16,6 +16,7 @@
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
+import org.springframework.context.annotation.Lazy;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -47,6 +48,7 @@
@Resource
private CrmContractMapper contractMapper;
@Resource
+ @Lazy // 寤惰繜鍔犺浇锛岄伩鍏嶅惊鐜緷璧�
private CrmReceivableService receivableService;
@Resource
private AdminUserApi adminUserApi;
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
index 01b1a2d..b6df80f 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/CrmContractController.java
@@ -176,11 +176,19 @@
@PutMapping("/submit")
@Operation(summary = "鎻愪氦鍚堝悓瀹℃壒")
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
- public CommonResult<Boolean> submitContract(@RequestParam("id") Long id) {
- contractService.submitContract(id, getLoginUserId());
+ public CommonResult<Boolean> submitContract(@RequestParam("id") Long id,
+ @RequestParam("processDefinitionKey") String processDefinitionKey) {
+ contractService.submitContract(id, processDefinitionKey, getLoginUserId());
return success(true);
}
+ @GetMapping("/approve-process-list")
+ @Operation(summary = "鑾峰彇鍚堝悓瀹℃壒娴佺▼鍒楄〃")
+ @PreAuthorize("@ss.hasPermission('crm:contract:query')")
+ public CommonResult<List<Map<String, Object>>> getContractApproveProcessList() {
+ return success(contractService.getContractApproveProcessDefinitionList());
+ }
+
private List<CrmContractRespVO> buildContractDetailList(List<CrmContractDO> contractList) {
if (CollUtil.isEmpty(contractList)) {
return Collections.emptyList();
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java
index 23b17b2..d33143d 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractRespVO.java
@@ -88,6 +88,10 @@
@ExcelProperty("鍚堝悓閲戦")
private BigDecimal totalPrice;
+ @Schema(description = "瀹氶噾閲戦锛屽崟浣嶏細鍏�", example = "1000.00")
+ @ExcelProperty("瀹氶噾閲戦")
+ private BigDecimal depositPrice;
+
@Schema(description = "宸插洖娆鹃噾棰�", example = "5617")
@ExcelProperty("宸插洖娆鹃噾棰�")
private BigDecimal totalReceivablePrice;
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractSaveReqVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractSaveReqVO.java
index 1ef6471..5e7b2c0 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractSaveReqVO.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/contract/CrmContractSaveReqVO.java
@@ -69,6 +69,10 @@
@DiffLogField(name = "鍚堝悓閲戦")
private BigDecimal totalPrice;
+ @Schema(description = "瀹氶噾閲戦锛屽崟浣嶏細鍏�", example = "1000.00")
+ @DiffLogField(name = "瀹氶噾閲戦")
+ private BigDecimal depositPrice;
+
@Schema(description = "瀹㈡埛绛剧害浜虹紪鍙�", example = "18546")
@DiffLogField(name = "瀹㈡埛绛剧害浜�", function = CrmContactParseFunction.NAME)
private Long signContactId;
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/CrmSaleQuotationController.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/CrmSaleQuotationController.java
index 1414b4b..2bad9a7 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/CrmSaleQuotationController.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/CrmSaleQuotationController.java
@@ -13,6 +13,8 @@
import cn.iocoder.yudao.module.crm.controller.admin.quotation.vo.CrmSaleQuotationSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.quotation.CrmSaleQuotationDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.quotation.CrmSaleQuotationProductDO;
+import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
+import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.crm.service.quotation.CrmSaleQuotationService;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
@@ -61,6 +63,8 @@
private MdmItemApi mdmItemApi;
@Resource
private MdmUnitMeasureApi mdmUnitMeasureApi;
+ @Resource
+ private CrmCustomerService customerService;
@PostMapping("/create")
@Operation(summary = "鍒涘缓閿�鍞姤浠峰崟")
@@ -135,22 +139,6 @@
ExcelUtils.write(response, "閿�鍞姤浠峰崟.xls", "鏁版嵁", CrmSaleQuotationRespVO.class, list);
}
- @PutMapping("/submit")
- @Operation(summary = "鎻愪氦閿�鍞姤浠峰鎵�")
- @PreAuthorize("@ss.hasPermission('crm:sale-quotation:submit')")
- public CommonResult<Boolean> submitSaleQuotation(@RequestParam("id") Long id,
- @RequestParam("processDefinitionKey") String processDefinitionKey) {
- saleQuotationService.submitSaleQuotation(id, processDefinitionKey, getLoginUserId());
- return success(true);
- }
-
- @GetMapping("/approve-process-list")
- @Operation(summary = "鑾峰彇閿�鍞姤浠峰鎵规祦绋嬪垪琛�")
- @PreAuthorize("@ss.hasPermission('crm:sale-quotation:query')")
- public CommonResult<List<Map<String, Object>>> getSaleQuotationApproveProcessList() {
- return success(saleQuotationService.getSaleQuotationApproveProcessDefinitionList());
- }
-
@PostMapping("/convert-contract")
@Operation(summary = "杞负鍚堝悓")
@Parameter(name = "id", description = "鎶ヤ环鍗曠紪鍙�", required = true)
@@ -180,7 +168,14 @@
MapUtils.findAndThen(unitMap, item.getItemUnitId(), unit -> item.setItemUnitName(unit.getName()));
}));
}
- // 2. 濉厖鍏宠仈淇℃伅
+ // 2. 濉厖瀹㈡埛鍚嶇О
+ if (saleQuotation.getCustomerId() != null) {
+ CrmCustomerDO customer = customerService.getCustomer(saleQuotation.getCustomerId());
+ if (customer != null) {
+ respVO.setCustomerName(customer.getName());
+ }
+ }
+ // 3. 濉厖鍏宠仈淇℃伅
fillSaleQuotationRelatedFields(respVO);
return respVO;
}
@@ -195,10 +190,13 @@
Map<Long, List<CrmSaleQuotationProductDO>> productMap = convertMultiMap(productList, CrmSaleQuotationProductDO::getQuotationId);
// 1.2 鐗╂枡淇℃伅
Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(convertSet(productList, CrmSaleQuotationProductDO::getItemId));
- // 1.3 鐢ㄦ埛淇℃伅
+ // 1.3 瀹㈡埛淇℃伅
+ Map<Long, CrmCustomerDO> customerMap = customerService.getCustomerMap(
+ convertSet(pageResult.getList(), CrmSaleQuotationDO::getCustomerId));
+ // 1.4 鐢ㄦ埛淇℃伅
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(pageResult.getList(), CrmSaleQuotationDO::getOwnerUserId));
- // 1.4 閮ㄩ棬淇℃伅
+ // 1.5 閮ㄩ棬淇℃伅
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
convertSet(pageResult.getList(), CrmSaleQuotationDO::getOwnerUserId));
@@ -207,6 +205,7 @@
vo.setItems(BeanUtils.toBean(productMap.get(vo.getId()), CrmSaleQuotationRespVO.Item.class,
item -> MapUtils.findAndThen(itemMap, item.getItemId(), mdmItem -> item.setItemCode(mdmItem.getCode())
.setItemName(mdmItem.getName()).setItemBarCode(mdmItem.getBarCode()))));
+ MapUtils.findAndThen(customerMap, vo.getCustomerId(), customer -> vo.setCustomerName(customer.getName()));
MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname()));
MapUtils.findAndThen(deptMap, vo.getOwnerUserId(), dept -> vo.setOwnerUserDeptName(dept.getName()));
});
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java
index e7e3bae..61c072b 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/contract/CrmContractDO.java
@@ -104,6 +104,10 @@
*/
private BigDecimal totalPrice;
/**
+ * 瀹氶噾閲戦锛屽崟浣嶏細鍏�
+ */
+ private BigDecimal depositPrice;
+ /**
* 鍏宠仈 ERP 閿�鍞鍗曠紪鍙�
*/
private Long orderId;
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
index a61c925..b85d74c 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
@@ -118,6 +118,10 @@
return selectOne(CrmContractDO::getBusinessId, businessId);
}
+ default CrmContractDO selectByOrderId(Long orderId) {
+ return selectOne(CrmContractDO::getOrderId, orderId);
+ }
+
default List<CrmContractDO> selectListByCustomerId(Long customerId) {
return selectList(CrmContractDO::getCustomerId, customerId);
}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
index 4149e75..490a690 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -19,6 +19,7 @@
ErrorCode CONTRACT_GENERATE_ORDER_FAIL_NOT_APPROVE = new ErrorCode(1_020_000_006, "鐢熸垚閿�鍞鍗曞け璐ワ紝鍘熷洜锛氬悎鍚屾湭瀹℃壒閫氳繃");
ErrorCode CONTRACT_GENERATE_ORDER_FAIL_EXISTS_ORDER = new ErrorCode(1_020_000_007, "鐢熸垚閿�鍞鍗曞け璐ワ紝鍘熷洜锛氬凡鍏宠仈閿�鍞鍗曘�恵}銆�");
ErrorCode CONTRACT_GENERATE_ORDER_FAIL_NO_PRODUCT = new ErrorCode(1_020_000_008, "鐢熸垚閿�鍞鍗曞け璐ワ紝鍘熷洜锛氬悎鍚屾病鏈変骇鍝佷俊鎭�");
+ ErrorCode CONTRACT_BPM_PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_020_000_009, "鍚堝悓瀹℃壒娴佺▼涓嶅瓨鍦ㄦ垨鏈縺娲伙紝璇锋鏌� BPM 閰嶇疆");
// ========== 绾跨储绠$悊 1-020-001-000 ==========
ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "绾跨储涓嶅瓨鍦�");
@@ -114,12 +115,8 @@
ErrorCode SALE_QUOTATION_NO_EXISTS = new ErrorCode(1_020_015_001, "閿�鍞姤浠峰崟鍙峰凡瀛樺湪");
ErrorCode SALE_QUOTATION_UPDATE_FAIL_NOT_DRAFT = new ErrorCode(1_020_015_002, "閿�鍞姤浠峰崟({})涓嶆槸鑽夌鐘舵�侊紝鏃犳硶淇敼");
ErrorCode SALE_QUOTATION_DELETE_FAIL_NOT_DRAFT = new ErrorCode(1_020_015_003, "閿�鍞姤浠峰崟({})涓嶆槸鑽夌鐘舵�侊紝鏃犳硶鍒犻櫎");
- ErrorCode SALE_QUOTATION_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_015_004, "閿�鍞姤浠峰崟涓嶆槸鑽夌鐘舵�侊紝鏃犳硶鎻愪氦瀹℃壒");
- ErrorCode SALE_QUOTATION_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_015_005, "閿�鍞姤浠峰崟涓嶆槸瀹℃壒涓姸鎬侊紝鏃犳硶鏇存柊瀹℃壒鐘舵��");
- ErrorCode SALE_QUOTATION_BPM_CATEGORY_NOT_EXISTS = new ErrorCode(1_020_015_006, "閿�鍞姤浠峰鎵瑰垎绫讳笉瀛樺湪锛岃妫�鏌� BPM 閰嶇疆");
- ErrorCode SALE_QUOTATION_BPM_PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_020_015_007, "閿�鍞姤浠峰鎵规祦绋嬩笉瀛樺湪鎴栨湭婵�娲伙紝璇锋鏌� BPM 閰嶇疆");
- ErrorCode SALE_QUOTATION_NOT_APPROVE = new ErrorCode(1_020_015_008, "閿�鍞姤浠峰崟鏈鎵归�氳繃锛屾棤娉曡浆鍚堝悓");
- ErrorCode SALE_QUOTATION_CONTRACT_EXISTS = new ErrorCode(1_020_015_009, "閿�鍞姤浠峰崟宸茶浆鍚堝悓锛岃鍕块噸澶嶆搷浣�");
- ErrorCode SALE_QUOTATION_ITEM_NOT_EXISTS = new ErrorCode(1_020_015_010, "鐗╂枡涓嶅瓨鍦ㄦ垨鏈惎鐢�");
+ ErrorCode SALE_QUOTATION_CONVERT_FAIL_NOT_DRAFT = new ErrorCode(1_020_015_004, "閿�鍞姤浠峰崟({})涓嶆槸鑽夌鐘舵�侊紝鏃犳硶杞悎鍚�");
+ ErrorCode SALE_QUOTATION_ITEM_NOT_EXISTS = new ErrorCode(1_020_015_005, "鐗╂枡涓嶅瓨鍦ㄦ垨鏈惎鐢�");
+ ErrorCode SALE_QUOTATION_CONTRACT_EXISTS = new ErrorCode(1_020_015_006, "閿�鍞姤浠峰崟宸茶浆鍚堝悓锛岃鍕块噸澶嶆搷浣�");
}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java
index 3203481..b23de46 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmAuditStatusEnum.java
@@ -18,8 +18,9 @@
DRAFT(0, "鏈彁浜�"),
PROCESS(10, "瀹℃壒涓�"),
APPROVE(20, "瀹℃牳閫氳繃"),
- REJECT(30, "瀹℃牳涓嶉�氳繃"),
- CANCEL(40, "宸插彇娑�");
+ REJECT(30, "瀹℃牳涓嶉�氳繃"),
+ CANCEL(40, "宸插彇娑�"),
+ INVALID(50, "宸蹭綔搴�");
private final Integer status;
private final String name;
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/quotation/CrmSaleQuotationStatusEnum.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/quotation/CrmSaleQuotationStatusEnum.java
index e8029d4..884c7d5 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/quotation/CrmSaleQuotationStatusEnum.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/quotation/CrmSaleQuotationStatusEnum.java
@@ -7,7 +7,7 @@
import java.util.Arrays;
/**
- * CRM 閿�鍞姤浠峰鎵圭姸鎬佹灇涓�
+ * CRM 閿�鍞姤浠风姸鎬佹灇涓�
*
* @author 鑺嬮亾婧愮爜
*/
@@ -16,11 +16,7 @@
public enum CrmSaleQuotationStatusEnum implements ArrayValuable<Integer> {
DRAFT(0, "鑽夌"),
- PROCESS(10, "瀹℃壒涓�"),
- APPROVE(20, "宸查�氳繃"),
- REJECT(30, "宸叉嫆缁�"),
- CANCEL(40, "宸插彇娑�"),
- CONVERTED(50, "宸茶浆鍚堝悓");
+ CONVERTED(10, "宸茶浆鍚堝悓");
private final Integer status;
private final String name;
@@ -33,4 +29,4 @@
return ARRAYS;
}
-}
\ No newline at end of file
+}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
index a3e6ba0..80693c2 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
@@ -67,10 +67,18 @@
/**
* 鍙戣捣鍚堝悓瀹℃壒娴佺▼
*
- * @param id 鍚堝悓缂栧彿
- * @param userId 鐢ㄦ埛缂栧彿
+ * @param id 鍚堝悓缂栧彿
+ * @param processDefinitionKey 娴佺▼瀹氫箟 Key
+ * @param userId 鐢ㄦ埛缂栧彿
*/
- void submitContract(Long id, Long userId);
+ void submitContract(Long id, String processDefinitionKey, Long userId);
+
+ /**
+ * 鑾峰彇鍚堝悓瀹℃壒娴佺▼鍒楄〃
+ *
+ * @return 娴佺▼瀹氫箟鍒楄〃
+ */
+ List<Map<String, Object>> getContractApproveProcessDefinitionList();
/**
* 鏇存柊鍚堝悓娴佺▼瀹℃壒缁撴灉
@@ -211,4 +219,20 @@
*/
Long generateSaleOrder(Long id, Long userId);
+ /**
+ * 鏇存柊鍚堝悓鐘舵��
+ *
+ * @param id 鍚堝悓缂栧彿
+ * @param status 鐘舵��
+ */
+ void updateContractStatus(Long id, Integer status);
+
+ /**
+ * 鏍规嵁閿�鍞鍗曠紪鍙锋洿鏂板悎鍚岀姸鎬�
+ *
+ * @param orderId 閿�鍞鍗曠紪鍙�
+ * @param status 鐘舵��
+ */
+ void updateContractStatusByOrderId(Long orderId, Integer status);
+
}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
index 1ce3f41..d875b2d 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -10,6 +10,9 @@
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
+import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractTransferReqVO;
@@ -49,10 +52,15 @@
import java.math.BigDecimal;
import java.time.LocalDateTime;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
+
+import org.flowable.engine.repository.ProcessDefinition;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
@@ -74,6 +82,11 @@
* BPM 鍚堝悓瀹℃壒娴佺▼鏍囪瘑
*/
public static final String BPM_PROCESS_DEFINITION_KEY = "crm-contract-audit";
+
+ /**
+ * BPM 鍚堝悓瀹℃壒鍒嗙被缂栫爜
+ */
+ private static final String CONTRACT_APPROVE_CATEGORY_CODE = "contract_approve";
@Resource
private CrmContractMapper contractMapper;
@@ -103,6 +116,10 @@
@Resource
private BpmProcessInstanceApi bpmProcessInstanceApi;
@Resource
+ private BpmCategoryService bpmCategoryService;
+ @Resource
+ private BpmProcessDefinitionService bpmProcessDefinitionService;
+ @Resource
private ErpSaleOrderApi saleOrderApi;
@Resource
private MdmItemApi mdmItemApi;
@@ -123,7 +140,9 @@
}
// 2.1 鎻掑叆鍚堝悓
- CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setNo(no);
+ CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class)
+ .setNo(no)
+ .setAuditStatus(CrmAuditStatusEnum.DRAFT.getStatus()); // 榛樿涓烘湭鎻愪氦鐘舵��
calculateTotalPrice(contract, contractProducts);
contractMapper.insert(contract);
// 2.2 鎻掑叆鍚堝悓鍏宠仈鍟嗗搧
@@ -308,23 +327,98 @@
@Transactional(rollbackFor = Exception.class)
@LogRecord(type = CRM_CONTRACT_TYPE, subType = CRM_CONTRACT_SUBMIT_SUB_TYPE, bizNo = "{{#id}}",
success = CRM_CONTRACT_SUBMIT_SUCCESS)
- public void submitContract(Long id, Long userId) {
+ public void submitContract(Long id, String processDefinitionKey, Long userId) {
// 1. 鏍¢獙鍚堝悓鏄惁鍦ㄥ鎵�
CrmContractDO contract = validateContractExists(id);
if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus())) {
throw exception(CONTRACT_SUBMIT_FAIL_NOT_DRAFT);
}
- // 2. 鍒涘缓鍚堝悓瀹℃壒娴佺▼瀹炰緥
- String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
- .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id)));
+ // 2. 鏍¢獙鍒嗙被鍜屾祦绋嬪畾涔夋槸鍚﹀瓨鍦�
+ validateContractApproveCategoryAndProcessDefinition();
- // 3. 鏇存柊鍚堝悓宸ヤ綔娴佺紪鍙�
+ // 3. 鏍¢獙浼犲叆鐨勬祦绋嬪畾涔� Key 鏄惁灞炰簬璇ュ垎绫讳笖澶勪簬婵�娲荤姸鎬�
+ ProcessDefinition processDefinition = bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey);
+ if (processDefinition == null) {
+ throw exception(CONTRACT_BPM_PROCESS_DEFINITION_NOT_EXISTS);
+ }
+
+ // 4. 鍒涘缓鍚堝悓瀹℃壒娴佺▼瀹炰緥
+ String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
+ .setProcessDefinitionKey(processDefinitionKey).setBusinessKey(String.valueOf(id)));
+
+ // 5. 鏇存柊鍚堝悓宸ヤ綔娴佺紪鍙�
contractMapper.updateById(new CrmContractDO().setId(id).setProcessInstanceId(processInstanceId)
.setAuditStatus(CrmAuditStatusEnum.PROCESS.getStatus()));
- // 3. 璁板綍鏃ュ織
+ // 6. 璁板綍鏃ュ織
LogRecordContext.putVariable("contractName", contract.getName());
+ }
+
+ /**
+ * 鏍¢獙 BPM 鍚堝悓瀹℃壒鍒嗙被鍜屾祦绋嬪畾涔夋槸鍚﹀瓨鍦�
+ */
+ private void validateContractApproveCategoryAndProcessDefinition() {
+ // 1. 鏍¢獙鍒嗙被鏄惁瀛樺湪
+ List<String> codes = Collections.singletonList(CONTRACT_APPROVE_CATEGORY_CODE);
+ Map<String, ?> categoryMap = bpmCategoryService.getCategoryMap(codes);
+ if (!categoryMap.containsKey(CONTRACT_APPROVE_CATEGORY_CODE)) {
+ throw exception(CONTRACT_BPM_PROCESS_DEFINITION_NOT_EXISTS);
+ }
+
+ // 2. 鏍¢獙鍒嗙被涓嬫槸鍚︽湁鍙敤鐨勬祦绋嬪畾涔�
+ List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
+ .getProcessDefinitionInfoListByCategory(CONTRACT_APPROVE_CATEGORY_CODE);
+ if (CollUtil.isEmpty(definitionInfoList)) {
+ throw exception(CONTRACT_BPM_PROCESS_DEFINITION_NOT_EXISTS);
+ }
+
+ // 3. 鏍¢獙鏄惁鏈夋縺娲荤姸鎬佺殑娴佺▼瀹氫箟
+ Set<String> processDefinitionIds = convertSet(definitionInfoList, BpmProcessDefinitionInfoDO::getProcessDefinitionId);
+ List<ProcessDefinition> processDefinitions = bpmProcessDefinitionService.getProcessDefinitionList(processDefinitionIds);
+ boolean hasActiveDefinition = processDefinitions.stream()
+ .anyMatch(pd -> !pd.isSuspended());
+ if (!hasActiveDefinition) {
+ throw exception(CONTRACT_BPM_PROCESS_DEFINITION_NOT_EXISTS);
+ }
+ }
+
+ @Override
+ public List<Map<String, Object>> getContractApproveProcessDefinitionList() {
+ // 1. 鏍¢獙鍒嗙被鍜屾祦绋嬪畾涔夋槸鍚﹀瓨鍦�
+ validateContractApproveCategoryAndProcessDefinition();
+
+ // 2. 鑾峰彇鍒嗙被涓嬬殑娴佺▼瀹氫箟淇℃伅
+ List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
+ .getProcessDefinitionInfoListByCategory(CONTRACT_APPROVE_CATEGORY_CODE);
+
+ // 3. 鑾峰彇娴佺▼瀹氫箟璇︽儏
+ Set<String> processDefinitionIds = convertSet(definitionInfoList, BpmProcessDefinitionInfoDO::getProcessDefinitionId);
+ List<ProcessDefinition> processDefinitions = bpmProcessDefinitionService.getProcessDefinitionList(processDefinitionIds);
+
+ // 4. 杩囨护锛氬彧淇濈暀婵�娲荤姸鎬侊紝涓旂浉鍚� key 鍙繚鐣欐渶鏂扮増鏈�
+ Map<String, ProcessDefinition> latestVersionMap = new HashMap<>();
+ for (ProcessDefinition pd : processDefinitions) {
+ if (pd.isSuspended()) {
+ continue;
+ }
+ ProcessDefinition existing = latestVersionMap.get(pd.getKey());
+ if (existing == null || pd.getVersion() > existing.getVersion()) {
+ latestVersionMap.put(pd.getKey(), pd);
+ }
+ }
+
+ // 5. 杩斿洖娴佺▼瀹氫箟鍒楄〃
+ List<Map<String, Object>> result = new ArrayList<>();
+ for (ProcessDefinition pd : latestVersionMap.values()) {
+ Map<String, Object> item = new HashMap<>();
+ item.put("id", pd.getId());
+ item.put("key", pd.getKey());
+ item.put("name", pd.getName());
+ item.put("version", pd.getVersion());
+ result.add(item);
+ }
+ return result;
}
@Override
@@ -462,7 +556,8 @@
createReqDTO.setContractNo(contract.getNo());
createReqDTO.setSaleUserId(contract.getOwnerUserId());
createReqDTO.setOrderTime(LocalDateTime.now().toString());
- createReqDTO.setDiscountPercent(contract.getDiscountPercent());
+ createReqDTO.setDiscountPercent(contract.getDiscountPercent() != null ? contract.getDiscountPercent() : BigDecimal.ZERO);
+ createReqDTO.setDepositPrice(contract.getDepositPrice() != null ? contract.getDepositPrice() : BigDecimal.ZERO);
createReqDTO.setRemark("鐢卞悎鍚屻��" + contract.getNo() + "銆戣嚜鍔ㄧ敓鎴�");
// 5. 鏋勫缓璁㈠崟椤�
@@ -494,4 +589,21 @@
return orderId;
}
+ @Override
+ public void updateContractStatus(Long id, Integer status) {
+ contractMapper.updateById(new CrmContractDO()
+ .setId(id)
+ .setAuditStatus(status));
+ }
+
+ @Override
+ public void updateContractStatusByOrderId(Long orderId, Integer status) {
+ CrmContractDO contract = contractMapper.selectByOrderId(orderId);
+ if (contract != null) {
+ contractMapper.updateById(new CrmContractDO()
+ .setId(contract.getId())
+ .setAuditStatus(status));
+ }
+ }
+
}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractStatusListener.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractStatusListener.java
index ce82858..3d425ad 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractStatusListener.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractStatusListener.java
@@ -1,31 +1,56 @@
package cn.iocoder.yudao.module.crm.service.contract.listener;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
-import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener;
+import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
+import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
+import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
-import cn.iocoder.yudao.module.crm.service.contract.CrmContractServiceImpl;
import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
- * 鍚堝悓瀹℃壒鐨勭粨鏋滅殑鐩戝惉鍣ㄥ疄鐜扮被
+ * 鍚堝悓瀹℃壒鐨勭粨鏋滅洃鍚櫒瀹炵幇绫�
+ *
+ * 閫氳繃 businessKey锛堝悎鍚孖D锛夊尮閰嶏紝鑰屼笉鏄浐瀹氱殑娴佺▼瀹氫箟 Key锛�
+ * 杩欐牱鍙互鏀寔鍒嗙被涓嬩换鎰� Key 鐨勬祦绋嬫ā鍨嬨��
*
* @author HUIHUI
*/
@Component
-public class CrmContractStatusListener extends BpmProcessInstanceStatusEventListener {
+@Slf4j
+public class CrmContractStatusListener implements ApplicationListener<BpmProcessInstanceStatusEvent> {
@Resource
private CrmContractService contractService;
+ @Resource
+ private CrmContractMapper contractMapper;
@Override
- public String getProcessDefinitionKey() {
- return CrmContractServiceImpl.BPM_PROCESS_DEFINITION_KEY;
- }
+ public void onApplicationEvent(BpmProcessInstanceStatusEvent event) {
+ // 閫氳繃 businessKey 鏌ヨ鍚堝悓
+ Long contractId;
+ try {
+ contractId = Long.parseLong(event.getBusinessKey());
+ } catch (NumberFormatException e) {
+ return; // businessKey 涓嶆槸鏁板瓧锛屽拷鐣�
+ }
- @Override
- protected void onEvent(BpmProcessInstanceStatusEvent event) {
- contractService.updateContractAuditStatus(Long.parseLong(event.getBusinessKey()), event.getStatus());
+ CrmContractDO contract = contractMapper.selectById(contractId);
+ if (contract == null) {
+ return; // 涓嶆槸鍚堝悓锛屽拷鐣�
+ }
+
+ // 鍙湁瀹℃壒涓姸鎬佺殑鍚堝悓鎵嶅鐞�
+ if (!CrmAuditStatusEnum.PROCESS.getStatus().equals(contract.getAuditStatus())) {
+ log.warn("[onApplicationEvent] 鍚堝悓({}) 涓嶅浜庡鎵逛腑鐘舵�侊紝褰撳墠鐘舵��: {}", contractId, contract.getAuditStatus());
+ return;
+ }
+
+ log.info("[onApplicationEvent] 鍚堝悓({}) 瀹℃壒鐘舵�佹洿鏂�: {}", contractId, event.getStatus());
+ // 鏇存柊瀹℃壒鐘舵��
+ contractService.updateContractAuditStatus(contractId, event.getStatus());
}
}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationService.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationService.java
index 3170988..2f15d35 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationService.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationService.java
@@ -8,7 +8,6 @@
import java.util.Collection;
import java.util.List;
-import java.util.Map;
/**
* CRM 閿�鍞姤浠峰崟 Service 鎺ュ彛
@@ -88,30 +87,6 @@
* @return 浜у搧鏄庣粏鍒楄〃
*/
List<CrmSaleQuotationProductDO> getSaleQuotationProductListByQuotationIds(Collection<Long> quotationIds);
-
- /**
- * 鎻愪氦閿�鍞姤浠峰崟瀹℃壒
- *
- * @param id 鎶ヤ环鍗曠紪鍙�
- * @param processDefinitionKey 娴佺▼瀹氫箟 Key
- * @param userId 鐢ㄦ埛缂栧彿
- */
- void submitSaleQuotation(Long id, String processDefinitionKey, Long userId);
-
- /**
- * 鏇存柊閿�鍞姤浠峰崟瀹℃壒鐘舵��
- *
- * @param id 鎶ヤ环鍗曠紪鍙�
- * @param bpmResult BPM 瀹℃壒缁撴灉
- */
- void updateSaleQuotationAuditStatus(Long id, Integer bpmResult);
-
- /**
- * 鑾峰彇閿�鍞姤浠峰鎵规祦绋嬪垪琛�
- *
- * @return 娴佺▼瀹氫箟鍒楄〃
- */
- List<Map<String, Object>> getSaleQuotationApproveProcessDefinitionList();
/**
* 杞负鍚堝悓
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java
index 6a3f4d8..e5ad277 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java
@@ -1,16 +1,9 @@
package cn.iocoder.yudao.module.crm.service.quotation;
import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
-import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
-import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
-import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
-import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService;
-import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.quotation.vo.CrmSaleQuotationPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.quotation.vo.CrmSaleQuotationSaveReqVO;
@@ -33,7 +26,6 @@
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
-import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -54,11 +46,6 @@
@Validated
@Slf4j
public class CrmSaleQuotationServiceImpl implements CrmSaleQuotationService {
-
- /**
- * 閿�鍞姤浠� BPM 鍒嗙被缂栫爜
- */
- private static final String SALE_QUOTATION_APPROVE_CATEGORY_CODE = "sale_quotation_approve";
@Resource
private CrmSaleQuotationMapper saleQuotationMapper;
@@ -81,13 +68,6 @@
private CrmPermissionService permissionService;
@Resource
private CrmContractService contractService;
-
- @Resource
- private BpmCategoryService bpmCategoryService;
- @Resource
- private BpmProcessDefinitionService bpmProcessDefinitionService;
- @Resource
- private BpmProcessInstanceApi bpmProcessInstanceApi;
@Override
@Transactional(rollbackFor = Exception.class)
@@ -186,7 +166,8 @@
throw exception(SALE_QUOTATION_ITEM_NOT_EXISTS);
}
item.setItemUnitId(mdmItem.getUnitMeasureId());
- item.setItemPrice(mdmItem.getSalesPrice());
+ // 鐗╂枡鍘熶环锛屽鏋� MDM 涓病鏈夊垯榛樿涓� 0
+ item.setItemPrice(mdmItem.getSalesPrice() != null ? mdmItem.getSalesPrice() : BigDecimal.ZERO);
if (item.getQuotationPrice() != null && item.getCount() != null) {
item.setTotalPrice(MoneyUtils.priceMultiply(item.getQuotationPrice(), item.getCount()));
if (item.getTaxPercent() != null) {
@@ -276,138 +257,11 @@
@Override
@Transactional(rollbackFor = Exception.class)
- public void submitSaleQuotation(Long id, String processDefinitionKey, Long userId) {
+ public Long convertToContract(Long id, Long userId) {
// 1. 鏍¢獙鎶ヤ环鍗曞瓨鍦ㄤ笖涓鸿崏绋跨姸鎬�
CrmSaleQuotationDO saleQuotation = validateSaleQuotationExists(id);
if (!CrmSaleQuotationStatusEnum.DRAFT.getStatus().equals(saleQuotation.getStatus())) {
- throw exception(SALE_QUOTATION_SUBMIT_FAIL_NOT_DRAFT);
- }
-
- // 2. 鏍¢獙鍒嗙被鍜屾祦绋嬪畾涔夋槸鍚﹀瓨鍦�
- validateSaleQuotationApproveCategoryAndProcessDefinition();
-
- // 3. 鏍¢獙浼犲叆鐨勬祦绋嬪畾涔� Key 鏄惁灞炰簬璇ュ垎绫讳笖澶勪簬婵�娲荤姸鎬�
- ProcessDefinition processDefinition = bpmProcessDefinitionService.getActiveProcessDefinition(processDefinitionKey);
- if (processDefinition == null) {
- throw exception(SALE_QUOTATION_BPM_PROCESS_DEFINITION_NOT_EXISTS);
- }
-
- // 4. 鍒涘缓娴佺▼瀹炰緥
- String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId,
- new BpmProcessInstanceCreateReqDTO()
- .setProcessDefinitionKey(processDefinitionKey)
- .setBusinessKey(String.valueOf(id)));
-
- // 5. 鏇存柊鎶ヤ环鍗曠姸鎬�
- saleQuotationMapper.updateById(new CrmSaleQuotationDO()
- .setId(id)
- .setProcessInstanceId(processInstanceId)
- .setStatus(CrmSaleQuotationStatusEnum.PROCESS.getStatus()));
- }
-
- /**
- * 鏍¢獙 BPM 閿�鍞姤浠峰鎵瑰垎绫诲拰娴佺▼瀹氫箟鏄惁瀛樺湪
- */
- private void validateSaleQuotationApproveCategoryAndProcessDefinition() {
- // 1. 鏍¢獙鍒嗙被鏄惁瀛樺湪
- List<String> codes = Collections.singletonList(SALE_QUOTATION_APPROVE_CATEGORY_CODE);
- Map<String, ?> categoryMap = bpmCategoryService.getCategoryMap(codes);
- if (!categoryMap.containsKey(SALE_QUOTATION_APPROVE_CATEGORY_CODE)) {
- throw exception(SALE_QUOTATION_BPM_CATEGORY_NOT_EXISTS);
- }
-
- // 2. 鏍¢獙鍒嗙被涓嬫槸鍚︽湁鍙敤鐨勬祦绋嬪畾涔�
- List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
- .getProcessDefinitionInfoListByCategory(SALE_QUOTATION_APPROVE_CATEGORY_CODE);
- if (CollUtil.isEmpty(definitionInfoList)) {
- throw exception(SALE_QUOTATION_BPM_PROCESS_DEFINITION_NOT_EXISTS);
- }
-
- // 3. 鏍¢獙鏄惁鏈夋縺娲荤姸鎬佺殑娴佺▼瀹氫箟
- Set<String> processDefinitionIds = convertSet(definitionInfoList, BpmProcessDefinitionInfoDO::getProcessDefinitionId);
- List<ProcessDefinition> processDefinitions = bpmProcessDefinitionService.getProcessDefinitionList(processDefinitionIds);
- boolean hasActiveDefinition = processDefinitions.stream()
- .anyMatch(pd -> !pd.isSuspended());
- if (!hasActiveDefinition) {
- throw exception(SALE_QUOTATION_BPM_PROCESS_DEFINITION_NOT_EXISTS);
- }
- }
-
- @Override
- public void updateSaleQuotationAuditStatus(Long id, Integer bpmResult) {
- // 1. 鏍¢獙鎶ヤ环鍗曞瓨鍦ㄤ笖涓哄鎵逛腑鐘舵��
- CrmSaleQuotationDO saleQuotation = validateSaleQuotationExists(id);
- if (!CrmSaleQuotationStatusEnum.PROCESS.getStatus().equals(saleQuotation.getStatus())) {
- log.error("[updateSaleQuotationAuditStatus] 鎶ヤ环鍗�({}) 涓嶅浜庡鎵逛腑", id);
- throw exception(SALE_QUOTATION_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
- }
-
- // 2. 杞崲瀹℃壒缁撴灉
- Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
- saleQuotationMapper.updateById(new CrmSaleQuotationDO()
- .setId(id)
- .setStatus(auditStatus));
- }
-
- /**
- * BPM 瀹℃壒缁撴灉杞崲
- */
- private Integer convertBpmResultToAuditStatus(Integer bpmResult) {
- Integer auditStatus = BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult)
- ? CrmSaleQuotationStatusEnum.APPROVE.getStatus()
- : BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult)
- ? CrmSaleQuotationStatusEnum.REJECT.getStatus()
- : BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)
- ? CrmSaleQuotationStatusEnum.CANCEL.getStatus() : null;
- Assert.notNull(auditStatus, "BPM 瀹℃壒缁撴灉({}) 杞崲澶辫触", bpmResult);
- return auditStatus;
- }
-
- @Override
- public List<Map<String, Object>> getSaleQuotationApproveProcessDefinitionList() {
- // 1. 鏍¢獙鍒嗙被鍜屾祦绋嬪畾涔夋槸鍚﹀瓨鍦�
- validateSaleQuotationApproveCategoryAndProcessDefinition();
-
- // 2. 鑾峰彇鍒嗙被涓嬬殑娴佺▼瀹氫箟淇℃伅
- List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
- .getProcessDefinitionInfoListByCategory(SALE_QUOTATION_APPROVE_CATEGORY_CODE);
-
- // 3. 鑾峰彇娴佺▼瀹氫箟璇︽儏
- Set<String> processDefinitionIds = convertSet(definitionInfoList, BpmProcessDefinitionInfoDO::getProcessDefinitionId);
- List<ProcessDefinition> processDefinitions = bpmProcessDefinitionService.getProcessDefinitionList(processDefinitionIds);
-
- // 4. 杩囨护锛氬彧淇濈暀婵�娲荤姸鎬侊紝涓旂浉鍚� key 鍙繚鐣欐渶鏂扮増鏈�
- Map<String, ProcessDefinition> latestVersionMap = new HashMap<>();
- for (ProcessDefinition pd : processDefinitions) {
- if (pd.isSuspended()) {
- continue;
- }
- ProcessDefinition existing = latestVersionMap.get(pd.getKey());
- if (existing == null || pd.getVersion() > existing.getVersion()) {
- latestVersionMap.put(pd.getKey(), pd);
- }
- }
-
- // 5. 杩斿洖娴佺▼瀹氫箟鍒楄〃
- List<Map<String, Object>> result = new ArrayList<>();
- for (ProcessDefinition pd : latestVersionMap.values()) {
- Map<String, Object> item = new HashMap<>();
- item.put("id", pd.getId());
- item.put("key", pd.getKey());
- item.put("name", pd.getName());
- item.put("version", pd.getVersion());
- result.add(item);
- }
- return result;
- }
-
- @Override
- @Transactional(rollbackFor = Exception.class)
- public Long convertToContract(Long id, Long userId) {
- // 1. 鏍¢獙鎶ヤ环鍗曞瓨鍦ㄤ笖宸插鎵归�氳繃
- CrmSaleQuotationDO saleQuotation = validateSaleQuotationExists(id);
- if (!CrmSaleQuotationStatusEnum.APPROVE.getStatus().equals(saleQuotation.getStatus())) {
- throw exception(SALE_QUOTATION_NOT_APPROVE);
+ throw exception(SALE_QUOTATION_CONVERT_FAIL_NOT_DRAFT, saleQuotation.getNo());
}
// 鏍¢獙鏄惁宸茶浆鍚堝悓
if (saleQuotation.getContractId() != null) {
@@ -437,7 +291,7 @@
});
contractVO.setProducts(contractProducts);
- // 4. 鍒涘缓鍚堝悓
+ // 4. 鍒涘缓鍚堝悓锛堜細鑷姩妫�娴嬪悎鍚屽鎵规祦绋嬪苟鎻愪氦瀹℃壒锛�
Long contractId = contractService.createContract(contractVO, userId);
// 5. 鏇存柊鎶ヤ环鍗曞叧鑱斾俊鎭�
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/listener/CrmSaleQuotationStatusListener.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/listener/CrmSaleQuotationStatusListener.java
deleted file mode 100644
index ee11882..0000000
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/listener/CrmSaleQuotationStatusListener.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package cn.iocoder.yudao.module.crm.service.quotation.listener;
-
-import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
-import cn.iocoder.yudao.module.crm.dal.dataobject.quotation.CrmSaleQuotationDO;
-import cn.iocoder.yudao.module.crm.dal.mysql.quotation.CrmSaleQuotationMapper;
-import cn.iocoder.yudao.module.crm.enums.quotation.CrmSaleQuotationStatusEnum;
-import cn.iocoder.yudao.module.crm.service.quotation.CrmSaleQuotationService;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.context.ApplicationListener;
-import org.springframework.stereotype.Component;
-
-/**
- * 閿�鍞姤浠峰鎵圭殑缁撴灉鐩戝惉鍣ㄥ疄鐜扮被
- *
- * 閫氳繃 businessKey锛堟姤浠峰崟ID锛夊尮閰嶏紝鑰屼笉鏄浐瀹氱殑娴佺▼瀹氫箟 Key锛�
- * 杩欐牱鍙互鏀寔鍒嗙被涓嬩换鎰� Key 鐨勬祦绋嬫ā鍨嬨��
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Component
-@Slf4j
-public class CrmSaleQuotationStatusListener implements ApplicationListener<BpmProcessInstanceStatusEvent> {
-
- @Resource
- private CrmSaleQuotationService saleQuotationService;
- @Resource
- private CrmSaleQuotationMapper saleQuotationMapper;
-
- @Override
- public void onApplicationEvent(BpmProcessInstanceStatusEvent event) {
- // 閫氳繃 businessKey 鏌ヨ鎶ヤ环鍗�
- Long quotationId;
- try {
- quotationId = Long.parseLong(event.getBusinessKey());
- } catch (NumberFormatException e) {
- return; // businessKey 涓嶆槸鏁板瓧锛屽拷鐣�
- }
-
- CrmSaleQuotationDO saleQuotation = saleQuotationMapper.selectById(quotationId);
- if (saleQuotation == null) {
- return; // 涓嶆槸閿�鍞姤浠凤紝蹇界暐
- }
-
- // 鍙湁瀹℃壒涓姸鎬佺殑鎶ヤ环鍗曟墠澶勭悊
- if (!CrmSaleQuotationStatusEnum.PROCESS.getStatus().equals(saleQuotation.getStatus())) {
- log.warn("[onApplicationEvent] 閿�鍞姤浠峰崟({}) 涓嶅浜庡鎵逛腑鐘舵�侊紝褰撳墠鐘舵��: {}", quotationId, saleQuotation.getStatus());
- return;
- }
-
- log.info("[onApplicationEvent] 閿�鍞姤浠峰崟({}) 瀹℃壒鐘舵�佹洿鏂�: {}", quotationId, event.getStatus());
- // 鏇存柊瀹℃壒鐘舵��
- saleQuotationService.updateSaleQuotationAuditStatus(quotationId, event.getStatus());
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
index 383a7d9..f0211bd 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
@@ -83,6 +83,24 @@
return success(true);
}
+ @PutMapping("/cancel")
+ @Operation(summary = "浣滃簾閿�鍞鍗�")
+ @Parameter(name = "id", description = "缂栧彿", required = true)
+ @PreAuthorize("@ss.hasPermission('erp:sale-order:cancel')")
+ public CommonResult<Boolean> cancelSaleOrder(@RequestParam("id") Long id) {
+ saleOrderService.cancelSaleOrder(id);
+ return success(true);
+ }
+
+ @PutMapping("/enable")
+ @Operation(summary = "閲嶆柊鍚敤閿�鍞鍗�")
+ @Parameter(name = "id", description = "缂栧彿", required = true)
+ @PreAuthorize("@ss.hasPermission('erp:sale-order:enable')")
+ public CommonResult<Boolean> enableSaleOrder(@RequestParam("id") Long id) {
+ saleOrderService.enableSaleOrder(id);
+ return success(true);
+ }
+
@DeleteMapping("/delete")
@Operation(summary = "鍒犻櫎閿�鍞鍗�")
@Parameter(name = "ids", description = "缂栧彿鏁扮粍", required = true)
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
index b1374e8..59a9ae9 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
@@ -33,6 +33,12 @@
@ExcelProperty("瀹㈡埛鍚嶇О")
private String customerName;
+ @Schema(description = "鍏宠仈鍚堝悓缂栧彿", example = "1")
+ private Long contractId;
+ @Schema(description = "鍚堝悓鍗曞彿", example = "HT001")
+ @ExcelProperty("鍚堝悓鍗曞彿")
+ private String contractNo;
+
@Schema(description = "缁撶畻璐︽埛缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "311.89")
@ExcelProperty("缁撶畻璐︽埛缂栧彿")
private Long accountId;
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
index 42c0174..81412f1 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
@@ -18,7 +18,8 @@
public enum ErpAuditStatus implements ArrayValuable<Integer> {
PROCESS(10, "鏈鏍�"), // 瀹℃牳涓�
- APPROVE(20, "宸插鏍�"); // 瀹℃牳閫氳繃
+ APPROVE(20, "宸插鏍�"), // 瀹℃牳閫氳繃
+ CANCEL(30, "宸蹭綔搴�"); // 宸蹭綔搴�/宸叉挙閿�
public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpAuditStatus::getStatus).toArray(Integer[]::new);
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
index c7ccb51..08d75e0 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
@@ -78,6 +78,12 @@
ErrorCode SALE_ORDER_PROCESS_FAIL_EXISTS_OUT = new ErrorCode(1_020_201_008, "鍙嶅鏍稿け璐ワ紝宸插瓨鍦ㄥ搴旂殑閿�鍞嚭搴撳崟");
ErrorCode SALE_ORDER_ITEM_RETURN_FAIL_OUT_EXCEED = new ErrorCode(1_020_201_009, "閿�鍞鍗曢」({})瓒呰繃鏈�澶у厑璁搁��璐ф暟閲�({})");
ErrorCode SALE_ORDER_PROCESS_FAIL_EXISTS_RETURN = new ErrorCode(1_020_201_010, "鍙嶅鏍稿け璐ワ紝宸插瓨鍦ㄥ搴旂殑閿�鍞��璐у崟");
+ ErrorCode SALE_ORDER_CANCEL_FAIL_NOT_APPROVE = new ErrorCode(1_020_201_011, "浣滃簾澶辫触锛岄攢鍞鍗�({})鏈鏍革紝鍙湁宸插鏍哥殑璁㈠崟鎵嶈兘浣滃簾");
+ ErrorCode SALE_ORDER_CANCEL_FAIL_EXISTS_OUT = new ErrorCode(1_020_201_012, "浣滃簾澶辫触锛屽凡瀛樺湪瀵瑰簲鐨勯攢鍞嚭搴撳崟");
+ ErrorCode SALE_ORDER_CANCEL_FAIL_EXISTS_RETURN = new ErrorCode(1_020_201_013, "浣滃簾澶辫触锛屽凡瀛樺湪瀵瑰簲鐨勯攢鍞��璐у崟");
+ ErrorCode SALE_ORDER_CANCEL_FAIL = new ErrorCode(1_020_201_014, "浣滃簾澶辫触锛岃鍒锋柊椤甸潰鍚庨噸璇�");
+ ErrorCode SALE_ORDER_ENABLE_FAIL_NOT_CANCEL = new ErrorCode(1_020_201_015, "鍚敤澶辫触锛岄攢鍞鍗�({})鏈綔搴燂紝鍙湁宸蹭綔搴熺殑璁㈠崟鎵嶈兘閲嶆柊鍚敤");
+ ErrorCode SALE_ORDER_ENABLE_FAIL = new ErrorCode(1_020_201_016, "鍚敤澶辫触锛岃鍒锋柊椤甸潰鍚庨噸璇�");
// ========== ERP 閿�鍞嚭搴擄紙1-030-202-000锛� ==========
ErrorCode SALE_OUT_NOT_EXISTS = new ErrorCode(1_020_202_000, "閿�鍞嚭搴撳崟涓嶅瓨鍦�");
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java
index c75f201..b715e27 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java
@@ -43,6 +43,20 @@
void updateSaleOrderStatus(Long id, Integer status);
/**
+ * 浣滃簾閿�鍞鍗�
+ *
+ * @param id 缂栧彿
+ */
+ void cancelSaleOrder(Long id);
+
+ /**
+ * 閲嶆柊鍚敤閿�鍞鍗�
+ *
+ * @param id 缂栧彿
+ */
+ void enableSaleOrder(Long id);
+
+ /**
* 鏇存柊閿�鍞鍗曠殑鍑哄簱鏁伴噺
*
* @param id 缂栧彿
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
index c3bf261..17c99a9 100644
--- a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
@@ -6,6 +6,7 @@
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
+import cn.iocoder.yudao.module.crm.api.contract.CrmContractApi;
import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
@@ -56,6 +57,8 @@
private ErpProductService productService;
@Resource
private CrmCustomerApi customerApi;
+ @Resource
+ private CrmContractApi contractApi;
@Resource
private ErpAccountService accountService;
@@ -147,11 +150,13 @@
throw exception(approve ? SALE_ORDER_APPROVE_FAIL : SALE_ORDER_PROCESS_FAIL);
}
// 1.3 瀛樺湪閿�鍞嚭搴撳崟锛屾棤娉曞弽瀹℃牳
- if (!approve && saleOrder.getOutCount().compareTo(BigDecimal.ZERO) > 0) {
+ BigDecimal outCount = saleOrder.getOutCount() != null ? saleOrder.getOutCount() : BigDecimal.ZERO;
+ if (!approve && outCount.compareTo(BigDecimal.ZERO) > 0) {
throw exception(SALE_ORDER_PROCESS_FAIL_EXISTS_OUT);
}
// 1.4 瀛樺湪閿�鍞��璐у崟锛屾棤娉曞弽瀹℃牳
- if (!approve && saleOrder.getReturnCount().compareTo(BigDecimal.ZERO) > 0) {
+ BigDecimal returnCount = saleOrder.getReturnCount() != null ? saleOrder.getReturnCount() : BigDecimal.ZERO;
+ if (!approve && returnCount.compareTo(BigDecimal.ZERO) > 0) {
throw exception(SALE_ORDER_PROCESS_FAIL_EXISTS_RETURN);
}
@@ -163,6 +168,62 @@
}
}
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void cancelSaleOrder(Long id) {
+ // 1.1 鏍¢獙瀛樺湪
+ ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);
+ // 1.2 鏍¢獙鐘舵�侊細鍙湁宸插鏍哥殑璁㈠崟鎵嶈兘浣滃簾
+ if (!ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) {
+ throw exception(SALE_ORDER_CANCEL_FAIL_NOT_APPROVE, saleOrder.getNo());
+ }
+ // 1.3 瀛樺湪閿�鍞嚭搴撳崟锛屾棤娉曚綔搴�
+ BigDecimal outCount = saleOrder.getOutCount() != null ? saleOrder.getOutCount() : BigDecimal.ZERO;
+ if (outCount.compareTo(BigDecimal.ZERO) > 0) {
+ throw exception(SALE_ORDER_CANCEL_FAIL_EXISTS_OUT);
+ }
+ // 1.4 瀛樺湪閿�鍞��璐у崟锛屾棤娉曚綔搴�
+ BigDecimal returnCount = saleOrder.getReturnCount() != null ? saleOrder.getReturnCount() : BigDecimal.ZERO;
+ if (returnCount.compareTo(BigDecimal.ZERO) > 0) {
+ throw exception(SALE_ORDER_CANCEL_FAIL_EXISTS_RETURN);
+ }
+
+ // 2. 鏇存柊鐘舵�佷负宸蹭綔搴�
+ int updateCount = saleOrderMapper.updateByIdAndStatus(id, saleOrder.getStatus(),
+ new ErpSaleOrderDO().setStatus(ErpAuditStatus.CANCEL.getStatus()));
+ if (updateCount == 0) {
+ throw exception(SALE_ORDER_CANCEL_FAIL);
+ }
+
+ // 3. 鍚屾鏇存柊鍏宠仈鍚堝悓鐘舵�佷负宸蹭綔搴�
+ if (saleOrder.getContractId() != null) {
+ contractApi.updateContractStatus(saleOrder.getContractId(), 50).getCheckedData(); // 50 = 宸蹭綔搴�
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void enableSaleOrder(Long id) {
+ // 1.1 鏍¢獙瀛樺湪
+ ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);
+ // 1.2 鏍¢獙鐘舵�侊細鍙湁宸蹭綔搴熺殑璁㈠崟鎵嶈兘閲嶆柊鍚敤
+ if (!ErpAuditStatus.CANCEL.getStatus().equals(saleOrder.getStatus())) {
+ throw exception(SALE_ORDER_ENABLE_FAIL_NOT_CANCEL, saleOrder.getNo());
+ }
+
+ // 2. 鏇存柊鐘舵�佷负宸插鏍�
+ int updateCount = saleOrderMapper.updateByIdAndStatus(id, saleOrder.getStatus(),
+ new ErpSaleOrderDO().setStatus(ErpAuditStatus.APPROVE.getStatus()));
+ if (updateCount == 0) {
+ throw exception(SALE_ORDER_ENABLE_FAIL);
+ }
+
+ // 3. 鍚屾鏇存柊鍏宠仈鍚堝悓鐘舵�佷负瀹℃牳閫氳繃
+ if (saleOrder.getContractId() != null) {
+ contractApi.updateContractStatus(saleOrder.getContractId(), 20).getCheckedData(); // 20 = 瀹℃牳閫氳繃
+ }
+ }
+
private List<ErpSaleOrderItemDO> validateSaleOrderItems(List<ErpSaleOrderSaveReqVO.Item> list) {
// 1. 鏍¢獙浜у搧瀛樺湪
List<ErpProductDO> productList = productService.validProductList(
--
Gitblit v1.9.3