feat: 维保发货

dev_1.0.3
chenhao 2026-09-30 16:17:54 +08:00
parent c81faba391
commit 70332d353c
18 changed files with 532 additions and 39 deletions

View File

@ -97,6 +97,15 @@ export function listProductSn(query) {
}) })
} }
// 查询仅维保订单发货可续用的SN(关联项目同产品编码的已发货SN)
export function maintenanceSn(query) {
return request({
url: '/inventory/delivery/vue/maintenanceSn',
method: 'get',
params: query
})
}
export function removeDelivery(id) { export function removeDelivery(id) {
return request({ return request({
url: '/inventory/delivery/vue/' + id, url: '/inventory/delivery/vue/' + id,

View File

@ -86,6 +86,15 @@ export function updateFinanceStatus(id, status) {
}) })
} }
// 查询仅维保订单可关联的项目订单(仅正常订单且审批完成)
export function relatedCandidateList(query) {
return request({
url: '/project/order/vue/relatedCandidateList',
method: 'get',
params: query
})
}
// 查询产品配货备货明细 // 查询产品配货备货明细
export function productMatchList(orderCode) { export function productMatchList(orderCode) {
return request({ return request({

View File

@ -89,6 +89,8 @@
<!-- 发货弹窗 --> <!-- 发货弹窗 -->
<generate-delivery-form ref="deliveryForm" @success="refreshTables"></generate-delivery-form> <generate-delivery-form ref="deliveryForm" @success="refreshTables"></generate-delivery-form>
<!-- 仅维保订单发货弹窗:直接续用关联项目已发货SN -->
<maintenance-delivery-form ref="maintenanceDeliveryForm" @success="refreshTables"></maintenance-delivery-form>
<!-- 查看详情弹窗 --> <!-- 查看详情弹窗 -->
<delivery-detail :delivery-id="deliveryId" :visible.sync="viewOpen" /> <delivery-detail :delivery-id="deliveryId" :visible.sync="viewOpen" />
<!-- 撤回审批查看弹窗 --> <!-- 撤回审批查看弹窗 -->
@ -130,12 +132,13 @@
import { changeOuterStatus, getOuter, queryInfo } from '@/api/inventory/outer'; import { changeOuterStatus, getOuter, queryInfo } from '@/api/inventory/outer';
import { removeDelivery, updateDeliveryStatus, recallDelivery, recallApply, checkRecallByDelivery } from '@/api/inventory/delivery'; import { removeDelivery, updateDeliveryStatus, recallDelivery, recallApply, checkRecallByDelivery } from '@/api/inventory/delivery';
import GenerateDeliveryForm from './GenerateDeliveryForm.vue'; import GenerateDeliveryForm from './GenerateDeliveryForm.vue';
import MaintenanceDeliveryForm from './MaintenanceDeliveryForm.vue';
import DeliveryDetail from '@/views/inventory/delivery/Detail.vue'; import DeliveryDetail from '@/views/inventory/delivery/Detail.vue';
import OuterRebackDetail from '@/views/approve/all/components/OuterRebackDetail.vue'; import OuterRebackDetail from '@/views/approve/all/components/OuterRebackDetail.vue';
export default { export default {
name: "OuterForm", name: "OuterForm",
components: { GenerateDeliveryForm, DeliveryDetail, OuterRebackDetail }, components: { GenerateDeliveryForm, MaintenanceDeliveryForm, DeliveryDetail, OuterRebackDetail },
props: { props: {
viewOnly: { viewOnly: {
type: Boolean, type: Boolean,
@ -238,7 +241,12 @@ export default {
...this.form, ...this.form,
notifierAddress: this.form.notifierAddress || row.notifierAddress || fallbackNotifierAddress || '' notifierAddress: this.form.notifierAddress || row.notifierAddress || fallbackNotifierAddress || ''
}; };
this.$refs.deliveryForm.open(row, formData); // 仅维保订单:SN直接续用关联项目已发货SN,走专用发货弹窗
if (String(row.orderType || this.form.orderType) === '2') {
this.$refs.maintenanceDeliveryForm.open(row, formData);
} else {
this.$refs.deliveryForm.open(row, formData);
}
}, },
// 取消发货 // 取消发货
handleDeleteDelivery(deliveryId) { handleDeleteDelivery(deliveryId) {

View File

@ -13,12 +13,13 @@
@select-project="handleSelectProject" @select-project="handleSelectProject"
@select-user="openSelectPeople" @select-user="openSelectPeople"
@select-partner="openSelectPartner" @select-partner="openSelectPartner"
@select-related-order="openSelectRelatedOrder"
/> />
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="配置信息" name="config"> <el-tab-pane label="配置信息" name="config">
<div style="max-height: 60vh; overflow-y: auto; padding: 15px;"> <div style="max-height: 60vh; overflow-y: auto; padding: 15px;">
<product-config :value="form" @input="handleProductConfigInput" :disabled="!isProjectSelected || isOrderApprovedOrInReview" :lock-tax-rate="isProjectSelected" /> <product-config :value="form" @input="handleProductConfigInput" :disabled="productConfigDisabled" :lock-tax-rate="isProjectSelected" />
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="流转过程" name="flow"> <el-tab-pane label="流转过程" name="flow">
@ -152,6 +153,7 @@
<select-project :visible.sync="selectProjectVisible" @project-selected="selectProject"/> <select-project :visible.sync="selectProjectVisible" @project-selected="selectProject"/>
<select-user :visible.sync="selectUserVisible" @user-selected="handleUserSelected"/> <select-user :visible.sync="selectUserVisible" @user-selected="handleUserSelected"/>
<select-partner :visible.sync="selectPartnerVisible" @partner-selected="handlePartnerSelected"/> <select-partner :visible.sync="selectPartnerVisible" @partner-selected="handlePartnerSelected"/>
<related-order-select-dialog :visible.sync="selectRelatedOrderVisible" :exclude-order-id="form.id" @select="handleRelatedOrderSelected"/>
<select-commit-type :visible.sync="selectCommitTypeVisible" :order-channel="form.orderChannel" @commit-type-selected="handleCommitTypeSelected"/> <select-commit-type :visible.sync="selectCommitTypeVisible" :order-channel="form.orderChannel" @commit-type-selected="handleCommitTypeSelected"/>
</div> </div>
</template> </template>
@ -172,6 +174,7 @@ import OrderInfo from '@/views/project/order/components/OrderInfo.vue';
import SelectProject from "@/views/project/info/SelectProject"; import SelectProject from "@/views/project/info/SelectProject";
import SelectUser from "@/views/system/user/selectUser"; import SelectUser from "@/views/system/user/selectUser";
import SelectPartner from "@/views/system/partner/selectPartner"; import SelectPartner from "@/views/system/partner/selectPartner";
import RelatedOrderSelectDialog from "@/views/project/order/components/RelatedOrderSelectDialog.vue";
import {isEmpty} from "@/utils/validate"; import {isEmpty} from "@/utils/validate";
export default { export default {
@ -183,6 +186,7 @@ export default {
SelectProject, SelectProject,
SelectUser, SelectUser,
SelectPartner, SelectPartner,
RelatedOrderSelectDialog,
}, },
props: { props: {
visible: { visible: {
@ -223,6 +227,7 @@ export default {
selectProjectVisible: false, selectProjectVisible: false,
selectUserVisible: false, selectUserVisible: false,
selectPartnerVisible: false, selectPartnerVisible: false,
selectRelatedOrderVisible: false,
selectCommitTypeVisible: false, selectCommitTypeVisible: false,
submitLoading: false, submitLoading: false,
// 当前激活的Tab // 当前激活的Tab
@ -251,6 +256,13 @@ export default {
notifierPhone: [{ required: true, message: "联系方式不能为空", trigger: "blur" }], notifierPhone: [{ required: true, message: "联系方式不能为空", trigger: "blur" }],
paymentMethod: [{ required: true, message: "付款方式不能为空", trigger: "change" }], paymentMethod: [{ required: true, message: "付款方式不能为空", trigger: "change" }],
paymentRatio: [{ required: true, message: "付款比例不能为空", trigger: "blur" }], paymentRatio: [{ required: true, message: "付款比例不能为空", trigger: "blur" }],
relatedOrderId: [{ validator: (rule, value, callback) => {
if (this.form.orderType === '2' && !value) {
callback(new Error('仅维保订单必须选择关联项目'));
} else {
callback();
}
}, trigger: 'change' }],
} }
}; };
}, },
@ -271,6 +283,17 @@ export default {
const isRebackApproved = this.form.orderStatus === '2' && this.form.rebackApproveStatus === '2'; const isRebackApproved = this.form.orderStatus === '2' && this.form.rebackApproveStatus === '2';
return !isRebackApproved && (this.form.orderStatus === '1' || this.form.orderStatus === '2'); return !isRebackApproved && (this.form.orderStatus === '1' || this.form.orderStatus === '2');
}, },
// 产品配置可编辑条件:原有口径是「已选所属项目且订单未审批/未在批」;
// 仅维保订单带出关联订单的软件配置后,即使还没选自己的所属项目也要能直接改(配置最终归属本项目)
productConfigDisabled() {
if (this.isOrderApprovedOrInReview) {
return true;
}
if (this.form.orderType === '2' && this.form.relatedOrderId) {
return false;
}
return !this.isProjectSelected;
},
uniqueVersions() { uniqueVersions() {
if (!this.approveLogList || this.approveLogList.length === 0) return []; if (!this.approveLogList || this.approveLogList.length === 0) return [];
const versions = [...new Set(this.approveLogList.map(log => log.extendField1))]; const versions = [...new Set(this.approveLogList.map(log => log.extendField1))];
@ -439,6 +462,7 @@ export default {
dutyEmail: null, dutyPhone: null, partnerName: null, level: null, partnerUserName: null, partnerEmail: null, dutyEmail: null, dutyPhone: null, partnerName: null, level: null, partnerUserName: null, partnerEmail: null,
partnerPhone: null, notifierAddress: null, notifier: null, notifierEmail: null, notifierPhone: null, partnerPhone: null, notifierAddress: null, notifier: null, notifierEmail: null, notifierPhone: null,
paymentMethod: null, paymentRatio: 0, paymentDescription: null, remark: null, paymentMethod: null, paymentRatio: 0, paymentDescription: null, remark: null,
orderType: null, relatedOrderId: null, relatedOrderCode: null, relatedProjectName: null,
softwareProjectProductInfoList: [], hardwareProjectProductInfoList: [], maintenanceProjectProductInfoList: [] softwareProjectProductInfoList: [], hardwareProjectProductInfoList: [], maintenanceProjectProductInfoList: []
}; };
this.orderBakFile = {}; this.orderBakFile = {};
@ -523,7 +547,14 @@ export default {
this.form.businessPerson = projectData.contactPerson; this.form.businessPerson = projectData.contactPerson;
this.form.businessEmail = projectData.contactEmail; this.form.businessEmail = projectData.contactEmail;
this.form.businessPhone = projectData.contactPhone; this.form.businessPhone = projectData.contactPhone;
this.form.softwareProjectProductInfoList = projectData.softwareProjectProductInfoList || []; const incomingSoftware = projectData.softwareProjectProductInfoList || [];
// 仅维保订单已带出关联订单的软件配置时,随后选自己的项目不能把它清空(新项目本就还没有产品行)
const keepRelatedSoftware = !incomingSoftware.length
&& this.form.orderType === '2' && !!this.form.relatedOrderId
&& (this.form.softwareProjectProductInfoList || []).length > 0;
if (!keepRelatedSoftware) {
this.form.softwareProjectProductInfoList = incomingSoftware;
}
this.form.hardwareProjectProductInfoList = projectData.hardwareProjectProductInfoList || []; this.form.hardwareProjectProductInfoList = projectData.hardwareProjectProductInfoList || [];
this.form.maintenanceProjectProductInfoList = projectData.maintenanceProjectProductInfoList || []; this.form.maintenanceProjectProductInfoList = projectData.maintenanceProjectProductInfoList || [];
this.form.industryType = projectData.industryType; this.form.industryType = projectData.industryType;
@ -531,6 +562,53 @@ export default {
this.selectProjectVisible = false; this.selectProjectVisible = false;
this.form.shipmentAmount = this.totalConfigAmount; this.form.shipmentAmount = this.totalConfigAmount;
}, },
openSelectRelatedOrder() {
this.selectRelatedOrderVisible = true;
},
// 选择关联项目:记录关联订单,并把关联订单的软件配置带入当前订单
// 复制出来的产品行必须清空 id/projectId,否则保存时会按关联项目的范围物理删除或更新产品行
handleRelatedOrderSelected(row) {
this.$set(this.form, 'relatedOrderId', row.id);
this.$set(this.form, 'relatedOrderCode', row.orderCode);
this.$set(this.form, 'relatedProjectName', row.projectName);
this.selectRelatedOrderVisible = false;
// 同一项目的产品配置本就共用,无需复制
if (row.projectId && this.form.projectId && row.projectId === this.form.projectId) {
this.$modal.msgSuccess('已关联项目:' + row.projectName);
this.validateRelatedOrderField();
return;
}
getOrder(row.id).then(response => {
const related = (response.data && response.data.projectOrderInfo) || {};
const softwareList = (related.softwareProjectProductInfoList || []).map(item => {
return Object.assign({}, item, { id: null, projectId: null });
});
if (softwareList.length === 0) {
this.$modal.msgWarning('关联项目未配置软件产品,请手动维护产品配置信息');
this.validateRelatedOrderField();
return;
}
const overwrite = () => {
this.form.softwareProjectProductInfoList = softwareList;
this.form.shipmentAmount = this.totalConfigAmount;
this.$modal.msgSuccess('已带出关联项目的软件配置');
this.validateRelatedOrderField();
};
if ((this.form.softwareProjectProductInfoList || []).length > 0) {
this.$modal.confirm('将使用关联项目的软件配置覆盖当前订单的软件产品,是否继续?').then(overwrite).catch(() => {});
} else {
overwrite();
}
});
},
// 关联项目为只读输入框,赋值后需手动触发校验
validateRelatedOrderField() {
this.$nextTick(() => {
if (this.$refs.form) {
this.$refs.form.validateField('relatedOrderId', () => {});
}
});
},
openSelectPeople() { openSelectPeople() {
this.selectUserVisible = true; this.selectUserVisible = true;
}, },

View File

@ -68,11 +68,30 @@
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="订单类型" prop="orderType"> <el-form-item label="订单类型" prop="orderType">
<el-select v-model="localOrderData.orderType" :disabled="isReadonly"> <el-select v-model="localOrderData.orderType" :disabled="isReadonly" @change="handleOrderTypeChange">
<el-option v-for="dict in dict.type.delivery_order_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option> <el-option v-for="dict in dict.type.delivery_order_type" :key="dict.value" :label="dict.label" :value="dict.value"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="8" v-if="localOrderData.orderType === '2'">
<el-form-item label="关联项目" prop="relatedOrderId">
<el-input
:value="relatedOrderText"
placeholder="请选择关联项目"
readonly
:disabled="isReadonly"
@click.native="openRelatedOrderDialog"
>
<el-button slot="append" icon="el-icon-search" :disabled="isReadonly" @click.stop="openRelatedOrderDialog"></el-button>
<el-button
v-if="!isReadonly && localOrderData.relatedOrderId"
slot="append"
icon="el-icon-delete"
@click.stop="clearRelatedOrder"
></el-button>
</el-input>
</el-form-item>
</el-col>
<el-col :span="8"> <el-col :span="8">
<el-form-item label="币种" prop="currencyType"> <el-form-item label="币种" prop="currencyType">
<el-select v-model="localOrderData.currencyType" placeholder="请选择币种" :disabled="isReadonly"> <el-select v-model="localOrderData.currencyType" placeholder="请选择币种" :disabled="isReadonly">
@ -290,6 +309,17 @@ export default {
} }
}, },
dicts:['delivery_order_type'], dicts:['delivery_order_type'],
computed: {
// 关联项目回显文本:关联订单编码 / 关联项目名称
relatedOrderText() {
const code = this.localOrderData.relatedOrderCode;
const name = this.localOrderData.relatedProjectName;
if (!code && !name) {
return '';
}
return [code, name].filter(item => !!item).join(' / ');
}
},
data() { data() {
return { return {
localOrderData: {}, localOrderData: {},
@ -331,6 +361,25 @@ export default {
}, },
methods: { methods: {
getDicts, getDicts,
// 切换订单类型:非仅维保时清空关联项目
handleOrderTypeChange(value) {
if (value !== '2') {
this.clearRelatedOrder();
}
},
// 打开关联项目选择弹窗,只读态不响应
openRelatedOrderDialog() {
if (this.isReadonly) {
return;
}
this.$emit('select-related-order');
},
// 清空关联项目:关联订单id/编码/项目名称一并清除,产品配置保持不动
clearRelatedOrder() {
this.$set(this.localOrderData, 'relatedOrderId', null);
this.$set(this.localOrderData, 'relatedOrderCode', null);
this.$set(this.localOrderData, 'relatedProjectName', null);
},
handleBgChange(value, isInitialLoad = false) { handleBgChange(value, isInitialLoad = false) {
if (!isInitialLoad) { if (!isInitialLoad) {
this.localOrderData.industryType = null; this.localOrderData.industryType = null;

View File

@ -106,6 +106,10 @@
<td class="label-cell">付款比例</td> <td class="label-cell">付款比例</td>
<td class="value-cell">{{ displayValue(localOrderData.paymentRatio, true) }}</td> <td class="value-cell">{{ displayValue(localOrderData.paymentRatio, true) }}</td>
</tr> </tr>
<tr v-if="localOrderData.orderType === '2'">
<td class="label-cell">关联项目</td>
<td class="value-cell" colspan="5">{{ displayValue(relatedText) }}</td>
</tr>
<tr> <tr>
<td class="label-cell">付款条件</td> <td class="label-cell">付款条件</td>
<td class="value-cell" colspan="5" style="white-space: pre-wrap;">{{ displayValue(localOrderData.paymentDescription) }}</td> <td class="value-cell" colspan="5" style="white-space: pre-wrap;">{{ displayValue(localOrderData.paymentDescription) }}</td>
@ -180,6 +184,16 @@ export default {
paymentMethodOptions: [], paymentMethodOptions: [],
}; };
}, },
computed: {
// 仅维保订单展示的关联项目:订单编号 / 项目名称
relatedText() {
if (!this.localOrderData.relatedOrderCode) { return ''; }
if (this.localOrderData.relatedProjectName) {
return this.localOrderData.relatedOrderCode + ' / ' + this.localOrderData.relatedProjectName;
}
return this.localOrderData.relatedOrderCode;
},
},
watch: { watch: {
orderData: { orderData: {
handler(newValue) { handler(newValue) {

View File

@ -75,6 +75,15 @@ public class VueDeliveryController extends BaseController {
return AjaxResult.success(inventoryDelivery); return AjaxResult.success(inventoryDelivery);
} }
/**
* 查询仅维保订单发货可续用的SN(关联项目下同产品编码的已发货SN)
*/
@RequiresPermissions("inventory:delivery:add")
@GetMapping("/maintenanceSn")
public AjaxResult maintenanceSn(@RequestParam String orderCode, @RequestParam String productCode) {
return AjaxResult.success(inventoryDeliveryService.getMaintenanceSnList(orderCode, productCode));
}
/** /**
* 新增发货记录 * 新增发货记录
*/ */

View File

@ -169,6 +169,19 @@ public class VueProjectOrderInfoController extends BaseController {
return AjaxResult.success(fileName); return AjaxResult.success(fileName);
} }
/**
* 仅维保订单「关联项目」候选订单:只返回已审批完成的正常订单,可排除自身
*/
@RequiresPermissions("project:order:list")
@GetMapping("/relatedCandidateList")
public TableDataInfo relatedCandidateList(@RequestParam(required = false) String projectCode,
@RequestParam(required = false) String projectName,
@RequestParam(required = false) Long excludeOrderId) {
startPage();
List<ProjectOrderInfo> list = projectOrderInfoService.selectRelatedCandidateList(projectCode, projectName, excludeOrderId);
return getDataTable(list);
}
/** /**
* 配备货产品信息 * 配备货产品信息
*/ */

View File

@ -73,6 +73,21 @@ public class ProjectOrderInfo extends BaseEntity {
private String customerUserName; private String customerUserName;
private String orderType; private String orderType;
/**
* 关联项目订单id:仅维保订单(orderType=2)必填,指向被关联的正常订单
*/
private Long relatedOrderId;
/**
* 关联项目订单编码(只读,联表带出)
*/
private String relatedOrderCode;
/**
* 关联项目名称(只读,联表带出)
*/
private String relatedProjectName;
/** /**
* 出货金额 * 出货金额
*/ */
@ -317,6 +332,25 @@ public class ProjectOrderInfo extends BaseEntity {
*/ */
private Integer operationVersion; private Integer operationVersion;
/**
* 订单类型 1:正常订单 2:仅维保
*/
@Getter
public enum OrderTypeEnum {
NORMAL("1", "正常订单"),
MAINTENANCE("2", "仅维保"),
;
private final String value;
private final String code;
OrderTypeEnum(String code, String value) {
this.code = code;
this.value = value;
}
}
@Getter @Getter
public enum OrderStatus { public enum OrderStatus {
WAIT_COMMIT("0", "待提交"), WAIT_COMMIT("0", "待提交"),

View File

@ -7,6 +7,7 @@ import com.ruoyi.sip.domain.InventoryDelivery;
import com.ruoyi.sip.dto.ApiDataQueryDto; import com.ruoyi.sip.dto.ApiDataQueryDto;
import com.ruoyi.sip.vo.DeliveryApproveVo; import com.ruoyi.sip.vo.DeliveryApproveVo;
import com.ruoyi.sip.vo.DeliveryInfoVo; import com.ruoyi.sip.vo.DeliveryInfoVo;
import com.ruoyi.sip.vo.MaintenanceSnVo;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
@ -71,6 +72,15 @@ public interface InventoryDeliveryMapper
List<DeliveryInfoVo> listSn(ApiDataQueryDto dto); List<DeliveryInfoVo> listSn(ApiDataQueryDto dto);
/**
* 查询指定订单指定产品编码下已发货的SN(仅维保订单续用)
*
* @param orderCode 关联订单编码
* @param productCode 产品编码
* @return 已发货SN列表
*/
List<MaintenanceSnVo> listMaintenanceSn(@Param("orderCode") String orderCode, @Param("productCode") String productCode);
List<DeliveryApproveVo> selectDeliveryApproveList(@Param("id") Long id); List<DeliveryApproveVo> selectDeliveryApproveList(@Param("id") Long id);
/** /**

View File

@ -2,6 +2,7 @@ package com.ruoyi.sip.mapper;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Map;
import com.ruoyi.sip.domain.InventoryDelivery; import com.ruoyi.sip.domain.InventoryDelivery;
import com.ruoyi.sip.domain.ProjectOrderInfo; import com.ruoyi.sip.domain.ProjectOrderInfo;
@ -38,6 +39,14 @@ public interface ProjectOrderInfoMapper
* @return 订单管理集合 * @return 订单管理集合
*/ */
public List<ProjectOrderInfo> selectProjectOrderInfoList(ProjectOrderInfo projectOrderInfo); public List<ProjectOrderInfo> selectProjectOrderInfoList(ProjectOrderInfo projectOrderInfo);
/**
* 仅维保订单「关联项目」候选订单:已审批完成的正常订单
*
* @param params projectCode 项目编码 / projectName 项目名称 / excludeOrderId 需排除的订单id
* @return 候选订单集合
*/
public List<ProjectOrderInfo> selectRelatedCandidateList(Map<String, Object> params);
public ProjectOrderInfo selectProjectOrderInfoByCode(String orderCode); public ProjectOrderInfo selectProjectOrderInfoByCode(String orderCode);
/** /**
@ -64,6 +73,14 @@ public interface ProjectOrderInfoMapper
*/ */
public int clearRebackApproveStatus(Long id); public int clearRebackApproveStatus(Long id);
/**
* 清空订单的关联项目(仅维保订单改回正常订单时使用)
*
* @param id 订单管理主键
* @return 结果
*/
public int clearRelatedOrderId(Long id);
/** /**
* 删除订单管理 * 删除订单管理
* *

View File

@ -6,6 +6,7 @@ import com.ruoyi.sip.domain.InventoryDelivery;
import com.ruoyi.sip.dto.ApiDataQueryDto; import com.ruoyi.sip.dto.ApiDataQueryDto;
import com.ruoyi.sip.dto.inventory.InventoryDeliveryDetailExcelDto; import com.ruoyi.sip.dto.inventory.InventoryDeliveryDetailExcelDto;
import com.ruoyi.sip.vo.DeliveryInfoVo; import com.ruoyi.sip.vo.DeliveryInfoVo;
import com.ruoyi.sip.vo.MaintenanceDeliveryVo;
/** /**
* 产品库存Service接口 * 产品库存Service接口
@ -110,4 +111,13 @@ public interface IInventoryDeliveryService
void generateServiceBillsWhenAllDelivered(String orderCode); void generateServiceBillsWhenAllDelivered(String orderCode);
/**
* 查询仅维保订单发货可续用的SN(关联项目下同产品编码的已发货SN)
*
* @param orderCode 仅维保订单编码
* @param productCode 产品编码
* @return 关联项目信息与可续用的SN列表
*/
MaintenanceDeliveryVo getMaintenanceSnList(String orderCode, String productCode);
} }

View File

@ -54,6 +54,16 @@ public interface IProjectOrderInfoService
* @return 结果 * @return 结果
*/ */
public int updateProjectOrderInfo(ProjectOrderInfo projectOrderInfo); public int updateProjectOrderInfo(ProjectOrderInfo projectOrderInfo);
/**
* 仅维保订单「关联项目」候选订单列表(已审批完成的正常订单)
*
* @param projectCode 项目编码,模糊匹配,可为空
* @param projectName 项目名称,模糊匹配,可为空
* @param excludeOrderId 需要排除的订单id(编辑时排除自身),可为空
* @return 候选订单集合
*/
List<ProjectOrderInfo> selectRelatedCandidateList(String projectCode, String projectName, Long excludeOrderId);
public int updateProjectOrderInfoByCode(ProjectOrderInfo projectOrderInfo); public int updateProjectOrderInfoByCode(ProjectOrderInfo projectOrderInfo);
/** /**

View File

@ -27,6 +27,8 @@ import com.ruoyi.sip.mapper.OmsPurchaseOrderMapMapper;
import com.ruoyi.sip.service.*; import com.ruoyi.sip.service.*;
import com.ruoyi.sip.vo.DeliveryApproveVo; import com.ruoyi.sip.vo.DeliveryApproveVo;
import com.ruoyi.sip.vo.DeliveryInfoVo; import com.ruoyi.sip.vo.DeliveryInfoVo;
import com.ruoyi.sip.vo.MaintenanceDeliveryVo;
import com.ruoyi.sip.vo.MaintenanceSnVo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.flowable.engine.RuntimeService; import org.flowable.engine.RuntimeService;
@ -145,6 +147,8 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService,
throw new ServiceException("发货清单为空,保存失败"); throw new ServiceException("发货清单为空,保存失败");
} }
//仅维保订单:发货SN直接沿用关联项目已发货的SN,不校验采购绑定、不回写SN库存信息
boolean maintenanceOnly = false;
if (StringUtils.isNotEmpty(inventoryDelivery.getOrderCode())) { if (StringUtils.isNotEmpty(inventoryDelivery.getOrderCode())) {
ProjectOrderInfo orderInfo = projectOrderInfoService.selectProjectOrderInfoByOrderCode(inventoryDelivery.getOrderCode()); ProjectOrderInfo orderInfo = projectOrderInfoService.selectProjectOrderInfoByOrderCode(inventoryDelivery.getOrderCode());
if (orderInfo == null) { if (orderInfo == null) {
@ -152,8 +156,11 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService,
} }
//版本号取订单的version_code //版本号取订单的version_code
inventoryDelivery.setVersionCode(orderInfo.getVersionCode()); inventoryDelivery.setVersionCode(orderInfo.getVersionCode());
maintenanceOnly = ProjectOrderInfo.OrderTypeEnum.MAINTENANCE.getCode().equals(orderInfo.getOrderType());
if (StringUtils.isNotEmpty(inventoryDelivery.getPurchaseNo())) { if (maintenanceOnly) {
validateMaintenanceDeliverySn(inventoryDelivery.getOrderCode(), inventoryDelivery.getProductCode(), productSnList);
} else if (StringUtils.isNotEmpty(inventoryDelivery.getPurchaseNo())) {
int newSnCount = CollUtil.isEmpty(productSnList) ? productSnDataList.size() : productSnList.size(); int newSnCount = CollUtil.isEmpty(productSnList) ? productSnDataList.size() : productSnList.size();
OmsPurchaseOrder purchaseOrder = omsPurchaseOrderService.selectOmsPurchaseOrderByNo(inventoryDelivery.getPurchaseNo()); OmsPurchaseOrder purchaseOrder = omsPurchaseOrderService.selectOmsPurchaseOrderByNo(inventoryDelivery.getPurchaseNo());
if (purchaseOrder == null) { if (purchaseOrder == null) {
@ -276,7 +283,10 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService,
return info; return info;
}).collect(Collectors.toList()); }).collect(Collectors.toList());
inventoryInfoService.saveBatch(inventoryInfoList); //仅维保发货只写发货单与发货明细,SN的库存状态、出库单号、订单归属保持关联项目原值
if (!maintenanceOnly) {
inventoryInfoService.saveBatch(inventoryInfoList);
}
} else { } else {
Long warehouseId = productSnDataList.get(0).getWarehouseId(); Long warehouseId = productSnDataList.get(0).getWarehouseId();
if (warehouseId!=null && !warehouseId.equals(inventoryDelivery.getWarehouseId())){ if (warehouseId!=null && !warehouseId.equals(inventoryDelivery.getWarehouseId())){
@ -615,9 +625,19 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService,
List<ProductInfo> productInfos = productInfoService.selectProductInfoList(productInfo); List<ProductInfo> productInfos = productInfoService.selectProductInfoList(productInfo);
productInfo.setType(ProductInfo.ProductTypeEnum.SERVICE.getType()); productInfo.setType(ProductInfo.ProductTypeEnum.SERVICE.getType());
productInfos.addAll(productInfoService.selectProductInfoList(productInfo)); productInfos.addAll(productInfoService.selectProductInfoList(productInfo));
// 设置服务等级和服务结束时间 // 设置服务等级和服务结束时间:同一SN+同一产品编码按订单排序(审批时间→创建时间→订单id升序),
for (DeliveryInfoVo deliveryInfoVo : resultList) { // 后一单(仅维保)的维保起始日取前一单最后一段维保截止日,实现维保日期续算
updateDeliveryInfoVo(deliveryInfoVo, maintenanceTypeMap, productInfos); Map<String, List<DeliveryInfoVo>> snGroupMap = resultList.stream().collect(Collectors.groupingBy(
item -> item.getSerialNumber() + "#" + item.getProductCode(), LinkedHashMap::new, Collectors.toList()));
for (List<DeliveryInfoVo> sameSnList : snGroupMap.values()) {
sameSnList.sort(maintenanceSnComparator());
Date carryStartTime = null;
for (DeliveryInfoVo deliveryInfoVo : sameSnList) {
Date lastServiceEndTime = updateDeliveryInfoVo(deliveryInfoVo, maintenanceTypeMap, productInfos, carryStartTime);
if (lastServiceEndTime != null) {
carryStartTime = lastServiceEndTime;
}
}
} }
return resultList; return resultList;
} }
@ -1473,10 +1493,16 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService,
/** /**
* 更新 DeliveryInfoVo 的服务等级和服务结束时间 * 更新 DeliveryInfoVo 的服务等级和服务结束时间
*/ */
private void updateDeliveryInfoVo(DeliveryInfoVo deliveryInfoVo, Map<Long, List<ProjectProductInfo>> maintenanceTypeMap, List<ProductInfo> productInfos) { /**
* 计算发货明细的维保等级与维保起止时间
*
* @param carryStartTime 续算起始日(同一SN上一单最后一段维保截止日),为空时按发货日期起算
* @return 本单最后一段维保截止日,无维保段时返回 null,供下一单续算
*/
private Date updateDeliveryInfoVo(DeliveryInfoVo deliveryInfoVo, Map<Long, List<ProjectProductInfo>> maintenanceTypeMap, List<ProductInfo> productInfos, Date carryStartTime) {
List<ProjectProductInfo> productInfoList = maintenanceTypeMap.get(deliveryInfoVo.getProjectId()); List<ProjectProductInfo> productInfoList = maintenanceTypeMap.get(deliveryInfoVo.getProjectId());
List<DeliveryInfoVo.ServiceInfo> serviceInfoList = new ArrayList<>(); List<DeliveryInfoVo.ServiceInfo> serviceInfoList = new ArrayList<>();
Date startTime = deliveryInfoVo.getServiceStartTime(); Date startTime = carryStartTime != null ? carryStartTime : deliveryInfoVo.getServiceStartTime();
if (CollUtil.isNotEmpty(productInfoList) && ProductInfo.ProductTypeEnum.SOFTWARE.getType().equals(deliveryInfoVo.getProductType())) { if (CollUtil.isNotEmpty(productInfoList) && ProductInfo.ProductTypeEnum.SOFTWARE.getType().equals(deliveryInfoVo.getProductType())) {
for (ProjectProductInfo productInfo : productInfoList) { for (ProjectProductInfo productInfo : productInfoList) {
if (ProductInfo.ProductTypeLevel2ServiceEnum.SOFTWARE_QA.getType().equals(productInfo.getLevel2Type()) && StringUtils.isNotEmpty(productInfo.getValue())) { if (ProductInfo.ProductTypeLevel2ServiceEnum.SOFTWARE_QA.getType().equals(productInfo.getLevel2Type()) && StringUtils.isNotEmpty(productInfo.getValue())) {
@ -1511,6 +1537,10 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService,
} }
} }
deliveryInfoVo.setServiceInfo(serviceInfoList); deliveryInfoVo.setServiceInfo(serviceInfoList);
if (CollUtil.isEmpty(serviceInfoList)) {
return null;
}
return serviceInfoList.get(serviceInfoList.size() - 1).getServiceEndTime();
} }
private Date updateStartTimeAndAddList(DeliveryInfoVo deliveryInfoVo, ProjectProductInfo productInfo, Date startTime, List<DeliveryInfoVo.ServiceInfo> serviceInfoList) { private Date updateStartTimeAndAddList(DeliveryInfoVo deliveryInfoVo, ProjectProductInfo productInfo, Date startTime, List<DeliveryInfoVo.ServiceInfo> serviceInfoList) {
@ -1525,4 +1555,57 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService,
serviceInfoList.add(serviceInfo); serviceInfoList.add(serviceInfo);
return startTime; return startTime;
} }
/**
* 同一SN+产品编码的续算排序:审批时间(为空回退创建时间)升序,完全相同按订单id升序
*/
private Comparator<DeliveryInfoVo> maintenanceSnComparator() {
Comparator<Date> dateComparator = Comparator.nullsLast(Comparator.naturalOrder());
return Comparator.comparing((DeliveryInfoVo vo) -> vo.getApproveTime() != null ? vo.getApproveTime() : vo.getCreateTime(), dateComparator)
.thenComparing(DeliveryInfoVo::getOrderId, Comparator.nullsLast(Comparator.naturalOrder()));
}
/**
* 查询仅维保订单发货可续用的SN(关联项目下同产品编码的已发货SN)
*/
@Override
public MaintenanceDeliveryVo getMaintenanceSnList(String orderCode, String productCode) {
if (StringUtils.isEmpty(orderCode) || StringUtils.isEmpty(productCode)) {
throw new ServiceException("订单编码和产品编码不能为空");
}
ProjectOrderInfo orderInfo = projectOrderInfoService.selectProjectOrderInfoByOrderCode(orderCode);
if (orderInfo == null) {
throw new ServiceException("订单信息不存在");
}
if (!ProjectOrderInfo.OrderTypeEnum.MAINTENANCE.getCode().equals(orderInfo.getOrderType())) {
throw new ServiceException("当前订单不是仅维保订单,无需续用关联项目SN");
}
if (orderInfo.getRelatedOrderId() == null) {
throw new ServiceException("当前仅维保订单未配置关联项目,无法发货");
}
ProjectOrderInfo relatedOrder = projectOrderInfoService.selectProjectOrderInfoById(orderInfo.getRelatedOrderId());
if (relatedOrder == null) {
throw new ServiceException("关联项目订单不存在");
}
List<MaintenanceSnVo> snList = inventoryDeliveryMapper.listMaintenanceSn(relatedOrder.getOrderCode(), productCode);
MaintenanceDeliveryVo vo = new MaintenanceDeliveryVo();
vo.setRelatedOrderId(relatedOrder.getId());
vo.setRelatedOrderCode(relatedOrder.getOrderCode());
vo.setRelatedProjectName(relatedOrder.getProjectName());
vo.setSnList(snList == null ? new ArrayList<>() : snList);
vo.setAvailableNum(CollUtil.isEmpty(snList) ? 0 : snList.size());
return vo;
}
/**
* 校验仅维保订单发货的SN必须来自关联项目同产品编码的已发货SN
*/
private void validateMaintenanceDeliverySn(String orderCode, String productCode, List<String> productSnList) {
MaintenanceDeliveryVo maintenanceVo = getMaintenanceSnList(orderCode, productCode);
List<String> availableSnList = maintenanceVo.getSnList().stream()
.map(MaintenanceSnVo::getProductSn).collect(Collectors.toList());
if (CollUtil.isEmpty(productSnList) || !availableSnList.containsAll(productSnList)) {
throw new ServiceException("仅维保订单只能发关联项目已发货的SN,请重新选择发货清单");
}
}
} }

View File

@ -329,6 +329,7 @@ public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService, To
if (projectOrderInfo.getProjectId() == null) { if (projectOrderInfo.getProjectId() == null) {
throw new ServiceException("请选择项目"); throw new ServiceException("请选择项目");
} }
validateRelatedOrder(projectOrderInfo, projectOrderInfo.getOrderType());
List<ProjectOrderInfo> projectOrderInfos = this.selectProjectOrderInfoByProjectId(Collections.singletonList(projectOrderInfo.getProjectId())); List<ProjectOrderInfo> projectOrderInfos = this.selectProjectOrderInfoByProjectId(Collections.singletonList(projectOrderInfo.getProjectId()));
if (CollUtil.isNotEmpty(projectOrderInfos)) { if (CollUtil.isNotEmpty(projectOrderInfos)) {
throw new ServiceException("该项目存在订单流转,无法添加"); throw new ServiceException("该项目存在订单流转,无法添加");
@ -448,6 +449,15 @@ public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService, To
} }
projectInfoMapper.updateProjectInfo(projectInfo); projectInfoMapper.updateProjectInfo(projectInfo);
// 前端未回传订单类型时以库中已有类型为准,避免仅维保校验被绕过
String effectiveOrderType = StringUtils.isNotEmpty(projectOrderInfo.getOrderType())
? projectOrderInfo.getOrderType() : existProjectOrderInfo.getOrderType();
validateRelatedOrder(projectOrderInfo, effectiveOrderType);
if (!ProjectOrderInfo.OrderTypeEnum.MAINTENANCE.getCode().equals(effectiveOrderType)
&& existProjectOrderInfo.getRelatedOrderId() != null) {
//订单类型由仅维保改回正常:动态update不会写入null,需显式清空历史关联
projectOrderInfoMapper.clearRelatedOrderId(projectOrderInfo.getId());
}
//不更新订单编号 //不更新订单编号
projectOrderInfo.setOrderCode(null); projectOrderInfo.setOrderCode(null);
projectOrderInfo.setUpdateBy(ShiroUtils.getUserId().toString()); projectOrderInfo.setUpdateBy(ShiroUtils.getUserId().toString());
@ -529,6 +539,46 @@ public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService, To
return projectOrderInfoMapper.updateProjectOrderInfoByCode(projectOrderInfo); return projectOrderInfoMapper.updateProjectOrderInfoByCode(projectOrderInfo);
} }
@Override
public List<ProjectOrderInfo> selectRelatedCandidateList(String projectCode, String projectName, Long excludeOrderId) {
Map<String, Object> params = new HashMap<>();
params.put("projectCode", projectCode);
params.put("projectName", projectName);
params.put("excludeOrderId", excludeOrderId);
return projectOrderInfoMapper.selectRelatedCandidateList(params);
}
/**
* 校验「关联项目」:仅维保订单必须关联一个已审批完成的正常订单,且不能是订单自身;
* 非仅维保订单不保留关联项目。
*
* @param projectOrderInfo 待保存订单
* @param orderType 生效的订单类型(编辑时前端可能不回传)
*/
private void validateRelatedOrder(ProjectOrderInfo projectOrderInfo, String orderType) {
if (!ProjectOrderInfo.OrderTypeEnum.MAINTENANCE.getCode().equals(orderType)) {
projectOrderInfo.setRelatedOrderId(null);
return;
}
Long relatedOrderId = projectOrderInfo.getRelatedOrderId();
if (relatedOrderId == null) {
throw new ServiceException("仅维保订单必须选择关联项目");
}
if (relatedOrderId.equals(projectOrderInfo.getId())) {
throw new ServiceException("关联项目不能选择当前订单本身");
}
ProjectOrderInfo related = projectOrderInfoMapper.selectProjectOrderInfoById(relatedOrderId);
if (related == null) {
throw new ServiceException("关联项目订单不存在");
}
if (!ProjectOrderInfo.OrderTypeEnum.NORMAL.getCode().equals(related.getOrderType())) {
throw new ServiceException("仅维保订单只能关联正常订单");
}
if (!ProjectOrderInfo.OrderStatus.APPROVE_COMPLETE.getCode().equals(related.getOrderStatus())) {
throw new ServiceException("关联项目订单不处于审批完成状态,无法关联");
}
}
private String getFlowBusinessKey(String code, String processType) { private String getFlowBusinessKey(String code, String processType) {
if (StringUtils.isEmpty(code)) { if (StringUtils.isEmpty(code)) {
throw new ServiceException("流程BusinessKey为空"); throw new ServiceException("流程BusinessKey为空");

View File

@ -38,6 +38,17 @@ public class DeliveryInfoVo {
private Date serviceStartTime; private Date serviceStartTime;
private List<ServiceInfo> serviceInfo; private List<ServiceInfo> serviceInfo;
/**
* 订单审批完成时间:仅维保续算时用于订单排序,审批时间为空回退 createTime
*/
@JsonIgnore
private Date approveTime;
/**
* 订单创建时间
*/
@JsonIgnore
private Date createTime;
private Date lastUpdateTime; private Date lastUpdateTime;
@JsonIgnore @JsonIgnore
private Long projectId; private Long projectId;

View File

@ -85,51 +85,101 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where t3.order_code= #{orderCode} and t1.delivery_status = #{status} where t3.order_code= #{orderCode} and t1.delivery_status = #{status}
</select> </select>
<select id="listSn" resultType="com.ruoyi.sip.vo.DeliveryInfoVo"> <select id="listSn" resultType="com.ruoyi.sip.vo.DeliveryInfoVo">
SELECT SELECT * FROM (
t3.order_code, SELECT
CAST(ifnull(t5.version_code,0) + ifnull(t5.operation_version,0) AS SIGNED) AS version_code, t3.order_code,
t5.version_code as order_version_code, CAST(ifnull(t5.version_code,0) + ifnull(t5.operation_version,0) AS SIGNED) AS version_code,
t5.project_id , t5.version_code as order_version_code,
t2.product_sn as 'serialNumber' , t5.project_id ,
t2.product_code, t5.id as order_id,
t4.description as 'productDescription', t5.approve_time,
t4.type as 'productType', t5.create_time,
t1.delivery_time as service_start_time, d.product_sn as 'serialNumber' ,
t1.update_time as 'lastUpdateTime' t3.product_code,
FROM t4.description as 'productDescription',
oms_inventory_delivery t1 t4.type as 'productType',
inner JOIN oms_inventory_info t2 ON t1.outer_code = t2.outer_code t1.delivery_time as service_start_time,
inner join oms_inventory_outer t3 on t1.outer_code=t3.outer_code t1.update_time as 'lastUpdateTime'
inner join product_info t4 on t2.product_code=t4.product_code FROM
inner join project_order_info t5 on t3.order_code=t5.order_code oms_inventory_delivery t1
WHERE inner join oms_inventory_delivery_detail d on d.delivery_id = t1.id
t1.delivery_status = 1 inner join oms_inventory_outer t3 on t1.outer_code=t3.outer_code
inner join product_info t4 on t3.product_code=t4.product_code
inner join project_order_info t5 on t3.order_code=t5.order_code
WHERE t1.delivery_status = 1
union all
SELECT
t3.order_code,
CAST(ifnull(t5.version_code,0) + ifnull(t5.operation_version,0) AS SIGNED) AS version_code,
t5.version_code as order_version_code,
t5.project_id ,
t5.id as order_id,
t5.approve_time,
t5.create_time,
t2.product_sn as 'serialNumber' ,
t2.product_code,
t4.description as 'productDescription',
t4.type as 'productType',
t1.delivery_time as service_start_time,
t1.update_time as 'lastUpdateTime'
FROM
oms_inventory_delivery t1
inner JOIN oms_inventory_info t2 ON t1.outer_code = t2.outer_code
inner join oms_inventory_outer t3 on t1.outer_code=t3.outer_code
inner join product_info t4 on t2.product_code=t4.product_code
inner join project_order_info t5 on t3.order_code=t5.order_code
WHERE t1.delivery_status = 1
and not exists (select 1 from oms_inventory_delivery_detail dd where dd.delivery_id = t1.id)
) sn
WHERE 1 = 1
<choose> <choose>
<when test="queryStartTime!=null and queryEndTime!=null"> <when test="queryStartTime!=null and queryEndTime!=null">
and t1.update_time between #{queryStartTime} and #{queryEndTime} and sn.lastUpdateTime between #{queryStartTime} and #{queryEndTime}
</when> </when>
<when test="queryStartTime!=null"> <when test="queryStartTime!=null">
and t1.update_time <![CDATA[ >= ]]> #{queryStartTime} and sn.lastUpdateTime <![CDATA[ >= ]]> #{queryStartTime}
</when> </when>
<when test="queryEndTime!=null"> <when test="queryEndTime!=null">
and t1.update_time <![CDATA[ <= ]]> #{queryEndTime} and sn.lastUpdateTime <![CDATA[ <= ]]> #{queryEndTime}
</when> </when>
</choose> </choose>
<choose> <choose>
<when test="serialNumbers != null and serialNumbers.size > 0"> <when test="serialNumbers != null and serialNumbers.size > 0">
and t2.product_sn in and sn.serialNumber in
<foreach item="item" collection="serialNumbers" open="(" separator="," close=")"> <foreach item="item" collection="serialNumbers" open="(" separator="," close=")">
#{item} #{item}
</foreach> </foreach>
</when> </when>
<when test="serialNumber!=null and serialNumber!=''"> <when test="serialNumber!=null and serialNumber!=''">
and t2.product_sn = #{serialNumber} and sn.serialNumber = #{serialNumber}
</when> </when>
</choose> </choose>
order by sn.serialNumber, ifnull(sn.approve_time, sn.create_time), sn.order_id
</select> </select>
<select id="listMaintenanceSn" resultType="com.ruoyi.sip.vo.MaintenanceSnVo">
SELECT * FROM (
SELECT d.product_sn as productSn, t1.id as deliveryId, t1.delivery_time as deliveryTime
FROM oms_inventory_delivery t1
inner join oms_inventory_delivery_detail d on d.delivery_id = t1.id
inner join oms_inventory_outer t3 on t1.outer_code = t3.outer_code
WHERE t1.delivery_status = 1
and t3.order_code = #{orderCode}
and t3.product_code = #{productCode}
union all
SELECT t2.product_sn, t1.id, t1.delivery_time
FROM oms_inventory_delivery t1
inner join oms_inventory_info t2 on t1.outer_code = t2.outer_code
inner join oms_inventory_outer t3 on t1.outer_code = t3.outer_code
WHERE t1.delivery_status = 1
and t3.order_code = #{orderCode}
and t3.product_code = #{productCode}
and not exists (select 1 from oms_inventory_delivery_detail dd where dd.delivery_id = t1.id)
) sn
order by sn.deliveryTime, sn.deliveryId, sn.productSn
</select>
<insert id="insertInventoryDelivery" parameterType="InventoryDelivery" useGeneratedKeys="true" keyProperty="id"> <insert id="insertInventoryDelivery" parameterType="InventoryDelivery" useGeneratedKeys="true" keyProperty="id">
insert into oms_inventory_delivery insert into oms_inventory_delivery
<trim prefix="(" suffix=")" suffixOverrides=","> <trim prefix="(" suffix=")" suffixOverrides=",">

View File

@ -50,6 +50,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="operationVersion" column="operation_version" /> <result property="operationVersion" column="operation_version" />
<result property="orderStockingStatus" column="order_stocking_status" /> <result property="orderStockingStatus" column="order_stocking_status" />
<result property="rebackApproveStatus" column="reback_approve_status" /> <result property="rebackApproveStatus" column="reback_approve_status" />
<result property="relatedOrderId" column="related_order_id" />
<result property="relatedOrderCode" column="related_order_code" />
<result property="relatedProjectName" column="related_project_name" />
</resultMap> </resultMap>
<sql id="selectProjectOrderInfoVo"> <sql id="selectProjectOrderInfoVo">
@ -57,7 +60,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
shipment_amount, actual_purchase_amount, order_end_time, delivery_time, company_delivery, notifier,finance_status, shipment_amount, actual_purchase_amount, order_end_time, delivery_time, company_delivery, notifier,finance_status,
notifier_email, notifier_phone, duty, duty_email, duty_phone, order_channel, partner_code, supplier,notifier_address, notifier_email, notifier_phone, duty, duty_email, duty_phone, order_channel, partner_code, supplier,notifier_address,
remark, order_status, create_by, create_time, update_by, update_time,version_code,process_type,process_template,discount_fold,order_stocking_status, remark, order_status, create_by, create_time, update_by, update_time,version_code,process_type,process_template,discount_fold,order_stocking_status,
delivery_status,sign_status,outer_status,approve_time,payment_method,payment_ratio,payment_description,order_type,operation_version,reback_approve_status delivery_status,sign_status,outer_status,approve_time,payment_method,payment_ratio,payment_description,order_type,operation_version,reback_approve_status,related_order_id
from project_order_info t1 from project_order_info t1
</sql> </sql>
@ -67,7 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
t1.notifier_email, t1.notifier_phone, t1.duty, t1.duty_email, t1.duty_phone, t1.order_channel, t1.partner_code, t1.supplier, t1.notifier_email, t1.notifier_phone, t1.duty, t1.duty_email, t1.duty_phone, t1.order_channel, t1.partner_code, t1.supplier,
t1.remark, t1.order_status, t1.create_by, t1.create_time, t1.update_by, t1.update_time,t1.partner_user_name,t1.partner_email t1.remark, t1.order_status, t1.create_by, t1.create_time, t1.update_by, t1.update_time,t1.partner_user_name,t1.partner_email
,t1.partner_phone,t1.version_code,t1.process_type,t1.process_template,t1.discount_fold,t1.notifier_address,t1.finance_status, ,t1.partner_phone,t1.version_code,t1.process_type,t1.process_template,t1.discount_fold,t1.notifier_address,t1.finance_status,
t1.delivery_status,t1.sign_status,t1.outer_status,t1.approve_time,t1.payment_method,t1.payment_ratio,t1.payment_description,t1.operation_version,t1.order_stocking_status,t1.reback_approve_status t1.delivery_status,t1.sign_status,t1.outer_status,t1.approve_time,t1.payment_method,t1.payment_ratio,t1.payment_description,t1.operation_version,t1.order_stocking_status,t1.reback_approve_status,t1.related_order_id
,t2.project_code,t2.project_name,t2.project_desc,t2.software_info,t2.hardware_info,t2.terminal_peripheral,t2.server_configuration ,t2.project_code,t2.project_name,t2.project_desc,t2.software_info,t2.hardware_info,t2.terminal_peripheral,t2.server_configuration
,t2.management_version,t2.desktop_vm_os_version,t2.vm_spec_quantity,t2.key_problem,t2.joint_trial_result ,t2.management_version,t2.desktop_vm_os_version,t2.vm_spec_quantity,t2.key_problem,t2.joint_trial_result
,t2.province,t2.customer_name,t2.customer_code,t2.industry_type,t2.bg_property,t2.agent_code,t2.estimated_order_time ,t2.province,t2.customer_name,t2.customer_code,t2.industry_type,t2.bg_property,t2.agent_code,t2.estimated_order_time
@ -76,7 +79,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
,t4.agent_name ,t4.agent_name
,t5.user_name as duty_name ,t5.user_name as duty_name
,t6.charge_status,t6.biz_charge_date,t6.finance_charge_date ,t6.charge_status,t6.biz_charge_date,t6.finance_charge_date
,rel_order.order_code related_order_code,rel_project.project_name related_project_name
from project_order_info t1 from project_order_info t1
left join project_order_info rel_order on t1.related_order_id = rel_order.id
left join project_info rel_project on rel_order.project_id = rel_project.id
left join project_info t2 on t1.project_id = t2.id left join project_info t2 on t1.project_id = t2.id
left join partner_info t3 on t1.partner_code=t3.partner_code left join partner_info t3 on t1.partner_code=t3.partner_code
@ -85,6 +91,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
left join oms_finance_charge t6 on t1.order_code=t6.order_code left join oms_finance_charge t6 on t1.order_code=t6.order_code
</sql> </sql>
<!-- 仅维保订单「关联项目」候选:只能是已审批完成的正常订单,可按项目编码、项目名称模糊搜索 -->
<select id="selectRelatedCandidateList" parameterType="java.util.Map" resultType="com.ruoyi.sip.domain.ProjectOrderInfo">
<include refid="selectProjectOrderInfoRelationVo"/>
where ifnull(t1.order_type,'1') = '1' and t1.order_status = '2'
<if test="excludeOrderId != null">and t1.id != #{excludeOrderId}</if>
<if test="projectCode != null and projectCode != ''">
and t2.project_code like concat('%',#{projectCode},'%')
</if>
<if test="projectName != null and projectName != ''">
and t2.project_name like concat('%',#{projectName},'%')
</if>
order by ifnull(t1.approve_time, t1.create_time) desc, t1.id desc
</select>
<select id="selectProjectOrderInfoList" parameterType="ProjectOrderInfo" resultMap="ProjectOrderInfoResult"> <select id="selectProjectOrderInfoList" parameterType="ProjectOrderInfo" resultMap="ProjectOrderInfoResult">
<include refid="selectProjectOrderInfoRelationVo"/> <include refid="selectProjectOrderInfoRelationVo"/>
where 1=1 where 1=1
@ -651,6 +671,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="paymentRatio != null">payment_ratio,</if> <if test="paymentRatio != null">payment_ratio,</if>
<if test="paymentDescription != null and paymentDescription != ''">payment_description,</if> <if test="paymentDescription != null and paymentDescription != ''">payment_description,</if>
<if test="operationVersion != null">operation_version,</if> <if test="operationVersion != null">operation_version,</if>
<if test="relatedOrderId != null">related_order_id,</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="projectId != null">#{projectId},</if> <if test="projectId != null">#{projectId},</if>
@ -695,6 +716,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="paymentRatio != null">#{paymentRatio},</if> <if test="paymentRatio != null">#{paymentRatio},</if>
<if test="paymentDescription != null and paymentDescription != ''">#{paymentDescription},</if> <if test="paymentDescription != null and paymentDescription != ''">#{paymentDescription},</if>
<if test="operationVersion != null">#{operationVersion},</if> <if test="operationVersion != null">#{operationVersion},</if>
<if test="relatedOrderId != null">#{relatedOrderId},</if>
</trim> </trim>
</insert> </insert>
<insert id="bakData"> <insert id="bakData">
@ -839,6 +861,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="financeStatus != null and financeStatus != ''">finance_status = #{financeStatus},</if> <if test="financeStatus != null and financeStatus != ''">finance_status = #{financeStatus},</if>
<if test="rebackApproveStatus != null and rebackApproveStatus != ''">reback_approve_status = #{rebackApproveStatus},</if> <if test="rebackApproveStatus != null and rebackApproveStatus != ''">reback_approve_status = #{rebackApproveStatus},</if>
<if test="operationVersion != null">operation_version = #{operationVersion},</if> <if test="operationVersion != null">operation_version = #{operationVersion},</if>
<if test="relatedOrderId != null">related_order_id = #{relatedOrderId},</if>
</trim> </trim>
where id = #{id} where id = #{id}
</update> </update>
@ -847,6 +870,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
set reback_approve_status = null set reback_approve_status = null
where id = #{id} where id = #{id}
</update> </update>
<!-- 订单类型由仅维保改回正常时清空关联项目:动态update不会写入null,需显式清空 -->
<update id="clearRelatedOrderId" parameterType="Long">
update project_order_info
set related_order_id = null
where id = #{id}
</update>
<update id="updateProjectOrderInfoByCode" parameterType="ProjectOrderInfo"> <update id="updateProjectOrderInfoByCode" parameterType="ProjectOrderInfo">
update project_order_info update project_order_info
<trim prefix="SET" suffixOverrides=","> <trim prefix="SET" suffixOverrides=",">