强化配送订单合同调度约束
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
// 功能描述:提供平台配送点资源管理与受控候选筛选。
|
||||
// 版本:v1.1.0
|
||||
package delivery
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
@@ -11,6 +14,19 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// parseDeliveryStatusFilter 解析可选的配送点通用状态筛选,拒绝归档和未知状态。
|
||||
func parseDeliveryStatusFilter(value string) (*int, bool) {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return nil, true
|
||||
}
|
||||
status, err := strconv.Atoi(value)
|
||||
if err != nil || !common.IsGenericRecordStatus(status) || status == common.StatusArchived {
|
||||
return nil, false
|
||||
}
|
||||
return &status, true
|
||||
}
|
||||
|
||||
// ListDeliveryBasic 查询配送点分页列表。
|
||||
func ListDeliveryBasic(ctx *gin.Context) {
|
||||
page, size := common.PageSize(ctx)
|
||||
@@ -22,6 +38,11 @@ func ListDeliveryBasic(ctx *gin.Context) {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
return
|
||||
}
|
||||
status, ok := parseDeliveryStatusFilter(ctx.Query("status"))
|
||||
if !ok {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
return
|
||||
}
|
||||
var list []models.DeliveryBasic
|
||||
var total int64
|
||||
query := common.ApplyKeywordFilter(ctx, common.ActiveRecords(impl.DBService.Model(&models.DeliveryBasic{})), &models.DeliveryBasic{})
|
||||
@@ -32,6 +53,9 @@ func ListDeliveryBasic(ctx *gin.Context) {
|
||||
common.StatusArchived,
|
||||
)
|
||||
}
|
||||
if status != nil {
|
||||
query = query.Where("delivery_basic.status = ?", *status)
|
||||
}
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
infra.Response.Error(ctx, err)
|
||||
return
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// 功能描述:验证平台配送点展示与候选筛选规则。
|
||||
// 版本:v1.1.0
|
||||
package delivery
|
||||
|
||||
import (
|
||||
@@ -37,6 +39,21 @@ func TestRestoreDeliveryBasicAddress(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseDeliveryStatusFilter 验证分配候选只接受明确的非归档通用状态。
|
||||
func TestParseDeliveryStatusFilter(t *testing.T) {
|
||||
if status, ok := parseDeliveryStatusFilter("1"); !ok || status == nil || *status != 1 {
|
||||
t.Fatalf("启用状态筛选解析失败:status=%v ok=%v", status, ok)
|
||||
}
|
||||
if status, ok := parseDeliveryStatusFilter(""); !ok || status != nil {
|
||||
t.Fatalf("空状态筛选应保持兼容:status=%v ok=%v", status, ok)
|
||||
}
|
||||
for _, value := range []string{"3", "enabled", "99"} {
|
||||
if _, ok := parseDeliveryStatusFilter(value); ok {
|
||||
t.Fatalf("非法配送点状态筛选被接受:%q", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewDeliveryAccountUsesAdminRole 验证平台创建配送点账号时固定后台唯一支持的角色编码。
|
||||
func TestNewDeliveryAccountUsesAdminRole(t *testing.T) {
|
||||
account := newDeliveryAccount(9, accountRequest{Username: "delivery-admin", DisplayName: "配送点管理员"}, "password-hash")
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// 功能描述:实现平台配送合同、配送订单及履约状态动作。
|
||||
// 版本:v1.2.0
|
||||
package gasorder
|
||||
|
||||
import (
|
||||
@@ -39,6 +41,12 @@ var (
|
||||
errOrderCreatorNoDelivery = errors.New("所选合同未指定配送点,不能以配送点作为创建方")
|
||||
errOrderCreatorDelivery = errors.New("创建方必须是配送合同指定的配送点")
|
||||
errOrderCreatorStaff = errors.New("所选工作人员不属于配送合同的履约组织或账号未启用")
|
||||
errAssignmentContractPoint = errors.New("配送合同已指定履约配送点,不能分配到其他配送点")
|
||||
errAssignmentCurrentPoint = errors.New("已分配订单只能在当前配送点内改派配送人员")
|
||||
errAssignmentDelivery = errors.New("所选配送点未启用或不属于订单履约气站")
|
||||
errAssignmentStaffScope = errors.New("所选配送人员不属于订单配送点或履约气站")
|
||||
errAssignmentStaffStatus = errors.New("所选工作人员不是启用且在岗的配送人员")
|
||||
errAssignmentCredential = errors.New("所选配送人员缺少启用且未过期的有效资质")
|
||||
)
|
||||
|
||||
// ListGasorderContract 按明确用途收窄合同候选;合同管理列表不带用途参数时保持全量查询。
|
||||
@@ -758,18 +766,6 @@ func AssignGasorderBasic(ctx *gin.Context) {
|
||||
}
|
||||
operatorIdentity, operatorName := common.PlatformOperator(ctx)
|
||||
err := impl.DBService.Transaction(func(tx *gorm.DB) error {
|
||||
var delivery models.DeliveryBasic
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Where("identity = ? AND status = ?", request.DeliveryIdentity, common.StatusEnable).
|
||||
First(&delivery).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
var staff models.StaffAccount
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Where("identity = ? AND status = ? AND work_status = ?", request.StaffIdentity, common.StatusEnable, "on_duty").
|
||||
First(&staff).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
var order models.GasorderBasic
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("identity = ?", ctx.Param("identity")).First(&order).Error; err != nil {
|
||||
return err
|
||||
@@ -777,8 +773,33 @@ func AssignGasorderBasic(ctx *gin.Context) {
|
||||
if order.OrderStatus != common.StatusCreated && order.OrderStatus != common.StatusAssigned {
|
||||
return errors.New("order cannot be assigned")
|
||||
}
|
||||
if !validGasorderAssignment(order, delivery, staff) {
|
||||
return errors.New("delivery or staff does not belong to the order organization")
|
||||
var contract models.GasorderContract
|
||||
if err := tx.Where("id = ?", order.GasorderContractID).First(&contract).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
var delivery models.DeliveryBasic
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Where("identity = ?", request.DeliveryIdentity).
|
||||
First(&delivery).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
var staff models.StaffAccount
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||
Where("identity = ?", request.StaffIdentity).
|
||||
First(&staff).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if businessErr := gasorderAssignmentValidationError(order, contract, delivery, staff, true); businessErr != nil {
|
||||
return businessErr
|
||||
}
|
||||
var credentialCount int64
|
||||
if err := tx.Model(&models.StaffCredential{}).
|
||||
Where("staff_account_id = ? AND status = ? AND (expired_at IS NULL OR expired_at > ?)", staff.ID, common.StatusEnable, time.Now()).
|
||||
Count(&credentialCount).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if credentialCount == 0 {
|
||||
return errAssignmentCredential
|
||||
}
|
||||
previous := order.OrderStatus
|
||||
if err := tx.Model(&order).Updates(map[string]any{
|
||||
@@ -801,7 +822,11 @@ func AssignGasorderBasic(ctx *gin.Context) {
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
if isGasorderAssignmentBusinessError(err) {
|
||||
infra.Response.Error(ctx, err)
|
||||
} else {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
}
|
||||
return
|
||||
}
|
||||
infra.Response.Success(ctx, gin.H{"updated": true, "order_status": common.StatusAssigned})
|
||||
@@ -994,14 +1019,37 @@ func releaseGasorderProducts(tx *gorm.DB, orderID uint64) error {
|
||||
return tx.Model(&models.GasorderItem{}).Where("gasorder_basic_id = ?", orderID).Update("active", false).Error
|
||||
}
|
||||
|
||||
func validGasorderAssignment(order models.GasorderBasic, delivery models.DeliveryBasic, staff models.StaffAccount) bool {
|
||||
if delivery.GasBasicID != 0 && delivery.GasBasicID != order.GasBasicID {
|
||||
return false
|
||||
// gasorderAssignmentValidationError 校验合同固定点、改派边界及配送人员准入条件。
|
||||
func gasorderAssignmentValidationError(order models.GasorderBasic, contract models.GasorderContract, delivery models.DeliveryBasic, staff models.StaffAccount, hasValidCredential bool) error {
|
||||
if contract.DeliveryBasicID != 0 && delivery.ID != contract.DeliveryBasicID {
|
||||
return errAssignmentContractPoint
|
||||
}
|
||||
if staff.DeliveryBasicID != delivery.ID {
|
||||
return false
|
||||
if contract.DeliveryBasicID == 0 && order.OrderStatus == common.StatusAssigned && order.DeliveryBasicID != 0 && delivery.ID != order.DeliveryBasicID {
|
||||
return errAssignmentCurrentPoint
|
||||
}
|
||||
return staff.GasBasicID == 0 || staff.GasBasicID == order.GasBasicID
|
||||
if delivery.Status != common.StatusEnable || delivery.GasBasicID != order.GasBasicID {
|
||||
return errAssignmentDelivery
|
||||
}
|
||||
if staff.RoleCode != "delivery" || staff.Status != common.StatusEnable || staff.WorkStatus != "on_duty" {
|
||||
return errAssignmentStaffStatus
|
||||
}
|
||||
if staff.DeliveryBasicID != delivery.ID || staff.GasBasicID != order.GasBasicID {
|
||||
return errAssignmentStaffScope
|
||||
}
|
||||
if !hasValidCredential {
|
||||
return errAssignmentCredential
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isGasorderAssignmentBusinessError 仅允许预定义分配错误返回前端,避免泄露数据库细节。
|
||||
func isGasorderAssignmentBusinessError(err error) bool {
|
||||
return errors.Is(err, errAssignmentContractPoint) ||
|
||||
errors.Is(err, errAssignmentCurrentPoint) ||
|
||||
errors.Is(err, errAssignmentDelivery) ||
|
||||
errors.Is(err, errAssignmentStaffScope) ||
|
||||
errors.Is(err, errAssignmentStaffStatus) ||
|
||||
errors.Is(err, errAssignmentCredential)
|
||||
}
|
||||
|
||||
func deliveryBelongsToGas(deliveryID, gasID uint64) bool {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
// 功能描述:验证平台配送合同、订单创建和调度状态规则。
|
||||
// 版本:v1.2.0
|
||||
package gasorder
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -198,19 +201,67 @@ func TestGasorderCompleteStateMachine(t *testing.T) {
|
||||
|
||||
func TestGasorderAssignmentRequiresSameOrganization(t *testing.T) {
|
||||
order := models.GasorderBasic{Entity: models.Entity{ID: 1}, GasBasicID: 10}
|
||||
delivery := models.DeliveryBasic{Entity: models.Entity{ID: 20}, GasBasicID: 10}
|
||||
staff := models.StaffAccount{GasBasicID: 10, DeliveryBasicID: 20}
|
||||
if !validGasorderAssignment(order, delivery, staff) {
|
||||
t.Fatal("valid organization assignment was rejected")
|
||||
contract := models.GasorderContract{GasBasicID: 10}
|
||||
delivery := models.DeliveryBasic{Entity: models.Entity{ID: 20, Status: common.StatusEnable}, GasBasicID: 10}
|
||||
staff := models.StaffAccount{Entity: models.Entity{Status: common.StatusEnable}, RoleCode: "delivery", WorkStatus: "on_duty", GasBasicID: 10, DeliveryBasicID: 20}
|
||||
if err := gasorderAssignmentValidationError(order, contract, delivery, staff, true); err != nil {
|
||||
t.Fatalf("合法组织分配被拒绝:%v", err)
|
||||
}
|
||||
staff.DeliveryBasicID = 21
|
||||
if validGasorderAssignment(order, delivery, staff) {
|
||||
t.Fatal("cross-delivery staff assignment was accepted")
|
||||
if err := gasorderAssignmentValidationError(order, contract, delivery, staff, true); !errors.Is(err, errAssignmentStaffScope) {
|
||||
t.Fatalf("跨配送点人员未被拒绝:%v", err)
|
||||
}
|
||||
staff.DeliveryBasicID = 20
|
||||
delivery.GasBasicID = 11
|
||||
if validGasorderAssignment(order, delivery, staff) {
|
||||
t.Fatal("cross-gas delivery assignment was accepted")
|
||||
if err := gasorderAssignmentValidationError(order, contract, delivery, staff, true); !errors.Is(err, errAssignmentDelivery) {
|
||||
t.Fatalf("跨气站配送点未被拒绝:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGasorderAssignmentRespectsContractAndQualification 覆盖合同固定点、改派、岗位状态和有效资质规则。
|
||||
func TestGasorderAssignmentRespectsContractAndQualification(t *testing.T) {
|
||||
baseOrder := models.GasorderBasic{Entity: models.Entity{ID: 1}, OrderStatus: common.StatusCreated, GasBasicID: 10}
|
||||
baseContract := models.GasorderContract{GasBasicID: 10, DeliveryBasicID: 20}
|
||||
baseDelivery := models.DeliveryBasic{Entity: models.Entity{ID: 20, Status: common.StatusEnable}, GasBasicID: 10}
|
||||
baseStaff := models.StaffAccount{Entity: models.Entity{Status: common.StatusEnable}, RoleCode: "delivery", WorkStatus: "on_duty", GasBasicID: 10, DeliveryBasicID: 20}
|
||||
tests := []struct {
|
||||
name string
|
||||
order models.GasorderBasic
|
||||
contract models.GasorderContract
|
||||
delivery models.DeliveryBasic
|
||||
staff models.StaffAccount
|
||||
hasCredential bool
|
||||
want error
|
||||
}{
|
||||
{"合同固定配送点", baseOrder, baseContract, baseDelivery, baseStaff, true, nil},
|
||||
{"跨合同配送点", baseOrder, baseContract, models.DeliveryBasic{Entity: models.Entity{ID: 21, Status: common.StatusEnable}, GasBasicID: 10}, baseStaff, true, errAssignmentContractPoint},
|
||||
{"无合同点首次选择", baseOrder, models.GasorderContract{GasBasicID: 10}, baseDelivery, baseStaff, true, nil},
|
||||
{"已分配订单跨点改派", models.GasorderBasic{OrderStatus: common.StatusAssigned, GasBasicID: 10, DeliveryBasicID: 20}, models.GasorderContract{GasBasicID: 10}, models.DeliveryBasic{Entity: models.Entity{ID: 21, Status: common.StatusEnable}, GasBasicID: 10}, baseStaff, true, errAssignmentCurrentPoint},
|
||||
{"配送人员离岗", baseOrder, baseContract, baseDelivery, models.StaffAccount{Entity: models.Entity{Status: common.StatusEnable}, RoleCode: "delivery", WorkStatus: "off_duty", GasBasicID: 10, DeliveryBasicID: 20}, true, errAssignmentStaffStatus},
|
||||
{"配送人员无有效资质", baseOrder, baseContract, baseDelivery, baseStaff, false, errAssignmentCredential},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := gasorderAssignmentValidationError(test.order, test.contract, test.delivery, test.staff, test.hasCredential)
|
||||
if !errors.Is(got, test.want) {
|
||||
t.Fatalf("分配校验结果 = %v,期望 %v", got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestGasorderAssignmentBusinessErrorsAreSafe 验证仅预定义中文分配错误允许返回客户端。
|
||||
func TestGasorderAssignmentBusinessErrorsAreSafe(t *testing.T) {
|
||||
for _, businessError := range []error{
|
||||
errAssignmentContractPoint, errAssignmentCurrentPoint, errAssignmentDelivery,
|
||||
errAssignmentStaffScope, errAssignmentStaffStatus, errAssignmentCredential,
|
||||
} {
|
||||
if !isGasorderAssignmentBusinessError(businessError) {
|
||||
t.Fatalf("分配业务错误未被识别:%v", businessError)
|
||||
}
|
||||
}
|
||||
if isGasorderAssignmentBusinessError(gorm.ErrInvalidDB) {
|
||||
t.Fatal("数据库错误不应透传给客户端")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
// 功能描述:提供平台工作人员资源管理与受控候选筛选。
|
||||
// 版本:v1.1.0
|
||||
package staff
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
"git.apinb.com/bsm-sdk/core/infra"
|
||||
@@ -14,9 +17,10 @@ import (
|
||||
)
|
||||
|
||||
type staffListFilters struct {
|
||||
RoleCodes []string
|
||||
Status *int
|
||||
WorkStatus string
|
||||
RoleCodes []string
|
||||
Status *int
|
||||
WorkStatus string
|
||||
ValidCredentialOnly bool
|
||||
}
|
||||
|
||||
type staffOrganizationFilters struct {
|
||||
@@ -27,7 +31,7 @@ type staffOrganizationFilters struct {
|
||||
}
|
||||
|
||||
// parseStaffListFilters 将工作人员列表查询参数收敛为闭集条件,拒绝含糊或冲突值。
|
||||
func parseStaffListFilters(roleCode, roleCodes, status, workStatus string) (staffListFilters, bool) {
|
||||
func parseStaffListFilters(roleCode, roleCodes, status, workStatus, validCredentialOnly string) (staffListFilters, bool) {
|
||||
filters := staffListFilters{}
|
||||
roleCode = strings.TrimSpace(roleCode)
|
||||
roleCodes = strings.TrimSpace(roleCodes)
|
||||
@@ -65,6 +69,11 @@ func parseStaffListFilters(roleCode, roleCodes, status, workStatus string) (staf
|
||||
return staffListFilters{}, false
|
||||
}
|
||||
filters.WorkStatus = workStatus
|
||||
validCredentialOnly = strings.TrimSpace(validCredentialOnly)
|
||||
if validCredentialOnly != "" && validCredentialOnly != "1" {
|
||||
return staffListFilters{}, false
|
||||
}
|
||||
filters.ValidCredentialOnly = validCredentialOnly == "1"
|
||||
return filters, true
|
||||
}
|
||||
|
||||
@@ -103,6 +112,7 @@ func ListStaff(ctx *gin.Context) {
|
||||
ctx.Query("role_codes"),
|
||||
ctx.Query("status"),
|
||||
ctx.Query("work_status"),
|
||||
ctx.Query("valid_credential_only"),
|
||||
)
|
||||
if !ok {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
@@ -131,6 +141,14 @@ func ListStaff(ctx *gin.Context) {
|
||||
if filters.WorkStatus != "" {
|
||||
query = query.Where("work_status = ?", filters.WorkStatus)
|
||||
}
|
||||
if filters.ValidCredentialOnly {
|
||||
now := time.Now()
|
||||
query = query.Where(
|
||||
"EXISTS (SELECT 1 FROM staff_credential WHERE staff_credential.staff_account_id = staff_account.id AND staff_credential.status = ? AND (staff_credential.expired_at IS NULL OR staff_credential.expired_at > ?))",
|
||||
common.StatusEnable,
|
||||
now,
|
||||
)
|
||||
}
|
||||
if organizationFilters.GasIdentity != "" {
|
||||
query = query.Where(
|
||||
"gas_basic_id IN (SELECT id FROM gas_basic WHERE identity = ? AND status <> ?)",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// 功能描述:验证工作人员角色、状态和组织候选筛选规则。
|
||||
// 版本:v1.1.0
|
||||
package staff
|
||||
|
||||
import "testing"
|
||||
@@ -25,30 +27,34 @@ func TestStaffRoleIsClosedEnumeration(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStaffListFiltersAcceptExplicitBusinessScopes(t *testing.T) {
|
||||
filters, ok := parseStaffListFilters("delivery", "", "1", "on_duty")
|
||||
filters, ok := parseStaffListFilters("delivery", "", "1", "on_duty", "1")
|
||||
if !ok || len(filters.RoleCodes) != 1 || filters.RoleCodes[0] != "delivery" {
|
||||
t.Fatal("delivery assignment filters were rejected")
|
||||
}
|
||||
if filters.Status == nil || *filters.Status != 1 || filters.WorkStatus != "on_duty" {
|
||||
t.Fatal("staff status filters were not preserved")
|
||||
}
|
||||
filters, ok = parseStaffListFilters("", "installer,delivery,operations", "1", "")
|
||||
if !filters.ValidCredentialOnly {
|
||||
t.Fatal("有效资质过滤条件未保留")
|
||||
}
|
||||
filters, ok = parseStaffListFilters("", "installer,delivery,operations", "1", "", "")
|
||||
if !ok || len(filters.RoleCodes) != 3 {
|
||||
t.Fatal("multi-role service-relation filters were rejected")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStaffListFiltersRejectAmbiguousOrUnknownValues(t *testing.T) {
|
||||
tests := [][4]string{
|
||||
{"delivery", "installer,delivery", "", ""},
|
||||
{"", "delivery,delivery", "", ""},
|
||||
{"admin", "", "", ""},
|
||||
{"", "", "3", ""},
|
||||
{"", "", "enabled", ""},
|
||||
{"", "", "", "available"},
|
||||
tests := [][5]string{
|
||||
{"delivery", "installer,delivery", "", "", ""},
|
||||
{"", "delivery,delivery", "", "", ""},
|
||||
{"admin", "", "", "", ""},
|
||||
{"", "", "3", "", ""},
|
||||
{"", "", "enabled", "", ""},
|
||||
{"", "", "", "available", ""},
|
||||
{"", "", "", "", "true"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if _, ok := parseStaffListFilters(test[0], test[1], test[2], test[3]); ok {
|
||||
if _, ok := parseStaffListFilters(test[0], test[1], test[2], test[3], test[4]); ok {
|
||||
t.Fatalf("invalid staff filters were accepted: %#v", test)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,7 +218,10 @@
|
||||
充装中/已就绪/配送中/待确认 -> 异常 -> 恢复到异常前状态
|
||||
```
|
||||
|
||||
- 分配时必须选择启用配送点和处于启用、在岗状态的工作人员。
|
||||
- 分配必须继承订单配送合同的履约范围:合同已指定配送点时自动带出并锁定该点;合同未指定配送点时,仅允许选择合同气站直属的启用配送点。
|
||||
- 配送人员候选仅包含目标配送点下角色为配送人员、账号启用、当前在岗且至少拥有一条启用并未过期资质的工作人员。
|
||||
- 已分配订单允许在当前配送点内改派配送人员,不允许通过改派更换配送点;每次分配或改派必须填写原因并保留分配历史。
|
||||
- 服务端必须重复校验合同固定配送点、订单当前配送点、人员组织、岗位状态和有效资质,并向正常业务冲突返回具体中文原因。
|
||||
- 开始充装前必须已完成配送点分配。
|
||||
- 每个状态动作要求当前状态合法,并追加状态记录。
|
||||
- 完成订单必须处于待确认状态,并提交确认类型、收件人和可选凭证。
|
||||
|
||||
54
docs/操作日志_配送订单合同调度约束_20260815.md
Normal file
54
docs/操作日志_配送订单合同调度约束_20260815.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# 操作日志:配送订单合同调度约束
|
||||
|
||||
操作时间:2026-08-15
|
||||
|
||||
操作类型:修改、扩展
|
||||
|
||||
影响模块:平台总后台、配送订单分配、配送点候选、配送人员候选
|
||||
|
||||
## 操作前状态
|
||||
|
||||
分配弹窗独立预加载全平台配送点和启用在岗配送人员,没有读取订单合同的履约气站或配送点,也没有按所选配送点重新加载人员。跨组织组合会在提交后被后端统一转换为“请求参数不正确”;后端未校验合同固定配送点和配送人员有效资质。
|
||||
|
||||
## 具体操作
|
||||
|
||||
- 分配弹窗读取订单履约气站和当前配送点;已有配送点时自动带出并锁定。
|
||||
- 合同未指定配送点时,配送点候选限定为订单气站直属启用配送点。
|
||||
- 配送点变化时清空旧配送人员,并按气站、配送点、配送角色、启用、在岗和有效资质重新加载。
|
||||
- 配送点及工作人员列表增加可选的精确候选筛选参数,不改变无参数管理列表行为。
|
||||
- 后端分配事务增加合同固定点、同点改派、配送点状态、人员组织、岗位状态和有效资质校验。
|
||||
- 将预定义业务冲突转换为具体中文错误,数据库错误继续使用通用脱敏错误。
|
||||
- 增加后端单元测试和前端静态联动检查。
|
||||
|
||||
## 代码变更
|
||||
|
||||
- `frontend/platform_admin/src/views/resource/ResourceActionDialog.vue`:分配动作初始化、配送点锁定和人员级联。
|
||||
- `frontend/platform_admin/src/api/resources.ts`:配送点启用筛选和人员有效资质策略。
|
||||
- `frontend/platform_admin/src/api/resource-staff-relation.ts`:有效资质查询参数。
|
||||
- `frontend/platform_admin/scripts/check-gasorder-assignment-linkage.mjs`:合同调度前端回归检查。
|
||||
- `backend/api/internal/logic/platform/gasorder/gasorder.go`:分配事务的合同与人员准入校验。
|
||||
- `backend/api/internal/logic/platform/gasorder/gasorder_test.go`:分配边界测试。
|
||||
- `backend/api/internal/logic/platform/delivery/delivery.go`:配送点状态候选筛选。
|
||||
- `backend/api/internal/logic/platform/staff/staff.go`:有效资质候选筛选。
|
||||
- `docs/05-平台总后台需求.md`:同步分配与改派业务基线。
|
||||
|
||||
## 操作后状态
|
||||
|
||||
订单只能在合同确定的履约范围内分配。合同有配送点时固定该点;合同无配送点时首次选择限于合同气站,后续改派锁定已选配送点。配送人员必须属于该点、启用、在岗且资质有效。
|
||||
|
||||
## 验证结果
|
||||
|
||||
- 后端 `gasorder`、`staff`、`delivery` 专项测试通过。
|
||||
- 后端 API 全量测试、`go vet` 和主程序构建通过。
|
||||
- 前端 TypeScript 类型检查通过。
|
||||
- 前端工作人员关系策略检查通过。
|
||||
- 前端配送订单分配联动检查通过。
|
||||
- 前端资源契约检查和生产构建通过。
|
||||
- 前端全仓 lint 未通过,失败来自既有模板文件的 2 个错误和 178 个警告;本次变更文件单独检查没有新增错误。
|
||||
|
||||
## 风险评估
|
||||
|
||||
- 本次未修改数据库结构、分配接口路径或请求字段,既有客户端保持兼容。
|
||||
- 候选列表新增的筛选参数均为可选参数;普通管理列表不携带时维持原行为。
|
||||
- 历史已分配订单沿用当前配送点并禁止跨点改派,可避免旧数据被静默迁移。
|
||||
- 若历史人员没有有效资质,将不再进入候选;需先在工作人员资质页面补充或启用有效记录。
|
||||
82
docs/项目文档_配送订单合同调度约束_v1.0.md
Normal file
82
docs/项目文档_配送订单合同调度约束_v1.0.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# 项目文档_配送订单合同调度约束_v1.0
|
||||
|
||||
## 1. 项目概述
|
||||
|
||||
本次修复平台总后台“分配订单”弹窗忽略订单配送合同和组织范围的问题。旧实现会独立加载配送点与配送人员,允许管理员组合跨气站或跨配送点数据,直到提交后才收到泛化的“请求参数不正确”。
|
||||
|
||||
修复后,分配动作继承订单创建时保存的合同履约气站和配送点快照,并由服务端执行相同的最终校验。
|
||||
|
||||
## 2. 业务规则
|
||||
|
||||
- 配送合同绑定签约用户、签约气站和可选配送点,不直接绑定配送人员。
|
||||
- 合同已指定配送点时,订单分配弹窗自动带出并锁定该点。
|
||||
- 合同未指定配送点时,仅展示合同气站直属且启用的配送点。
|
||||
- 选择配送点后,仅展示同时满足以下条件的工作人员:
|
||||
- 角色为配送人员;
|
||||
- 属于订单履约气站和目标配送点;
|
||||
- 账号启用且当前在岗;
|
||||
- 至少拥有一条启用且未过期的资质。
|
||||
- 已分配订单只允许在当前配送点内改派配送人员,不允许更换配送点。
|
||||
- 每次分配或改派继续填写原因,并追加不可覆盖的分配记录。
|
||||
- 前端候选限制不构成安全控制,服务端对合同范围、组织归属、岗位状态和有效资质进行重复校验。
|
||||
|
||||
## 3. 目录结构与核心文件
|
||||
|
||||
```text
|
||||
platforms/
|
||||
├── backend/api/internal/logic/platform/
|
||||
│ ├── gasorder/gasorder.go # 分配事务、合同范围和人员准入校验
|
||||
│ ├── gasorder/gasorder_test.go # 合同固定点、同点改派及资质回归测试
|
||||
│ ├── delivery/delivery.go # 启用配送点候选筛选
|
||||
│ └── staff/staff.go # 有效资质人员候选筛选
|
||||
├── frontend/platform_admin/
|
||||
│ ├── src/api/resources.ts # 分配动作候选规则
|
||||
│ ├── src/api/resource-staff-relation.ts # 有效资质筛选策略
|
||||
│ ├── src/views/resource/ResourceActionDialog.vue # 动作弹窗履约联动
|
||||
│ └── scripts/check-gasorder-assignment-linkage.mjs # 前端回归检查
|
||||
└── docs/ # 需求基线、项目文档与操作日志
|
||||
```
|
||||
|
||||
## 4. 接口行为
|
||||
|
||||
分配接口路径和请求字段保持不变:
|
||||
|
||||
```text
|
||||
POST /gasorder_basic/:identity/assign
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"delivery_basic_identity": "配送点唯一标识",
|
||||
"staff_account_identity": "配送人员唯一标识",
|
||||
"reason": "分配或改派原因"
|
||||
}
|
||||
```
|
||||
|
||||
候选列表新增兼容性查询条件:
|
||||
|
||||
- `/delivery_basic?gas_basic_identities=<气站标识>&status=1`
|
||||
- `/staff_account?gas_basic_identities=<气站标识>&delivery_basic_identities=<配送点标识>&role_code=delivery&status=1&work_status=on_duty&valid_credential_only=1`
|
||||
|
||||
未携带新增查询条件时,原有管理列表行为保持不变。
|
||||
|
||||
## 5. 行为变化
|
||||
|
||||
| 场景 | 修改前 | 修改后 |
|
||||
| --- | --- | --- |
|
||||
| 合同指定配送点 | 弹窗仍可选择其他配送点 | 自动带出并锁定合同配送点 |
|
||||
| 合同未指定配送点 | 可看到全平台配送点 | 仅展示合同气站直属启用配送点 |
|
||||
| 配送人员候选 | 仅按角色、启用和在岗筛选 | 追加气站、配送点和有效资质约束 |
|
||||
| 已分配订单改派 | 可再次提交不同配送点 | 只能在当前配送点内改派人员 |
|
||||
| 非法组合错误 | 统一显示请求参数不正确 | 返回合同点、组织、状态或资质的具体原因 |
|
||||
|
||||
## 6. 维护指南
|
||||
|
||||
- 调整合同配送点语义时,必须同步检查订单创建快照、分配弹窗和服务端分配校验。
|
||||
- 新增配送人员准入条件时,应同时更新人员候选查询和服务端事务校验。
|
||||
- 不得取消服务端校验或只依赖前端禁用状态。
|
||||
- 修改本功能后至少运行后端 `gasorder`、`staff`、`delivery` 单元测试,以及前端类型检查、分配联动检查和生产构建。
|
||||
|
||||
## 7. 版本记录
|
||||
|
||||
- v1.0(2026-08-15):建立合同履约范围内的订单分配与同点改派规则,补充有效资质约束和具体业务错误。
|
||||
@@ -26,6 +26,7 @@
|
||||
"gasorder-contract-candidates:check": "node scripts/check-gasorder-contract-candidates.mjs",
|
||||
"gasorder-contract-products:check": "node scripts/check-gasorder-contract-product-display.mjs",
|
||||
"gasorder-creator:check": "node scripts/check-gasorder-creator-linkage.mjs",
|
||||
"gasorder-assignment:check": "node scripts/check-gasorder-assignment-linkage.mjs",
|
||||
"gasorder-list-display:check": "node scripts/check-gasorder-list-display.mjs",
|
||||
"gasorder-status-display:check": "node scripts/check-gasorder-status-display.mjs",
|
||||
"product-ownership:check": "node scripts/check-product-ownership-action.mjs",
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* 功能:静态检查配送订单分配动作是否保留合同履约范围、配送点级联和有效资质约束。
|
||||
* 版本:v1.0.0
|
||||
*/
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
const resources = readFileSync(new URL('../src/api/resources.ts', import.meta.url), 'utf8');
|
||||
const dialog = readFileSync(new URL('../src/views/resource/ResourceActionDialog.vue', import.meta.url), 'utf8');
|
||||
const staffPolicy = readFileSync(new URL('../src/api/resource-staff-relation.ts', import.meta.url), 'utf8');
|
||||
|
||||
assert.match(resources, /resource: '\/gasorder_basic\/:identity\/assign'/, '缺少订单分配动作');
|
||||
assert.match(resources, /relationFilters: \{ status: '1' \}/, '配送点候选必须限定启用状态');
|
||||
assert.match(resources, /validCredentialOnly: true/, '配送人员候选必须限定有效资质');
|
||||
assert.match(dialog, /lockedAssignmentDeliveryIdentity/, '合同或订单已有配送点时必须锁定');
|
||||
assert.match(dialog, /assignmentDeliveryFilters/, '配送点候选必须按订单气站过滤');
|
||||
assert.match(dialog, /assignmentStaffFilters/, '配送人员候选必须按气站和配送点过滤');
|
||||
assert.match(dialog, /form\.staff_account_identity = undefined/, '切换配送点必须清空旧配送人员');
|
||||
assert.match(dialog, /initializeGasorderAssignment/, '分配弹窗不得预加载全平台候选');
|
||||
assert.match(staffPolicy, /valid_credential_only = '1'/, '有效资质策略必须转换为服务端筛选参数');
|
||||
|
||||
console.log('配送订单分配联动检查通过:配送点受合同与气站约束,配送人员按点和有效资质筛选。');
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* 功能:验证工作人员关联字段过滤、资质来源校验和路由上下文传递契约。
|
||||
* 版本:v1.1.0
|
||||
* 版本:v1.2.0
|
||||
*/
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
@@ -28,12 +28,18 @@ assert.deepEqual(
|
||||
roles: ['delivery'],
|
||||
enabledOnly: true,
|
||||
workStatus: 'on_duty',
|
||||
validCredentialOnly: true,
|
||||
},
|
||||
},
|
||||
{},
|
||||
),
|
||||
{ role_code: 'delivery', status: '1', work_status: 'on_duty' },
|
||||
'订单分配必须只查询启用且在岗的配送人员',
|
||||
{
|
||||
role_code: 'delivery',
|
||||
status: '1',
|
||||
work_status: 'on_duty',
|
||||
valid_credential_only: '1',
|
||||
},
|
||||
'订单分配必须只查询启用、在岗且资质有效的配送人员',
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* 功能:定义工作人员关联字段的显式过滤、角色展示与资质来源校验策略。
|
||||
* 版本:v1.0.0
|
||||
* 版本:v1.1.0
|
||||
*/
|
||||
|
||||
export const staffRoleCodes = ['installer', 'delivery', 'operations'] as const;
|
||||
@@ -11,6 +11,8 @@ export type StaffRelationPolicy = {
|
||||
roles: readonly StaffRoleCode[] | 'context';
|
||||
enabledOnly?: boolean;
|
||||
workStatus?: 'on_duty' | 'off_duty';
|
||||
/** 仅展示至少拥有一条启用且未过期资质的工作人员。 */
|
||||
validCredentialOnly?: boolean;
|
||||
lockPrefilled?: boolean;
|
||||
showIdentityCopy?: boolean;
|
||||
organizationScoped?: boolean;
|
||||
@@ -78,6 +80,9 @@ export function staffRelationFilters(
|
||||
if (field.staffRelation.workStatus) {
|
||||
filters.work_status = field.staffRelation.workStatus;
|
||||
}
|
||||
if (field.staffRelation.validCredentialOnly) {
|
||||
filters.valid_credential_only = '1';
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
||||
@@ -587,7 +587,7 @@ export const resources: ResourceUiDefinition[] = [
|
||||
parentChangeMessage: '配送合同已变更,请重新选择该合同用户的收货地址',
|
||||
},
|
||||
}), f('gasorder_contract_product_identities', { label: '合同气瓶', required: true, type: 'identity-list', listDisplayKey: 'contract_products_summary', listDetailLink: true, emptyText: '未填写', relation: '/gasorder_contract_product', placeholder: '请输入智能气阀名称、设备类型或设备编码搜索', relationOptionDisplay: 'product-name-type', relationEmptyText: '该合同暂无可用气瓶,请先为合同绑定气瓶', relationLinkage: { parentKey: 'gasorder_contract_identity', optionParentKey: '', filterKey: 'contract_identity', filterOnly: true, requiresParent: true, parentChangeMessage: '配送合同已变更,请重新选择该合同可用的气瓶' } }), f('contact_name', { required: true }), f('contact_phone', { required: true }), f('discount_amount'), f('remark')], 'list', [
|
||||
{ name: '分配订单', resource: '/gasorder_basic/:identity/assign', fields: [relation('delivery_basic_identity', '/delivery_basic', true), relation('staff_account_identity', '/staff_account', true, { staffRelation: { roles: ['delivery'], enabledOnly: true, workStatus: 'on_duty' } }), ...reason], visibleFor: { field: 'order_status', values: [16, 18] } },
|
||||
{ name: '分配订单', resource: '/gasorder_basic/:identity/assign', fields: [relation('delivery_basic_identity', '/delivery_basic', true, { label: '配送点', placeholder: '请选择合同履约范围内的配送点', relationFilters: { status: '1' } }), relation('staff_account_identity', '/staff_account', true, { label: '配送人员', placeholder: '请先选择配送点', relationEmptyText: '该配送点暂无在岗且资质有效的配送人员', staffRelation: { roles: ['delivery'], enabledOnly: true, workStatus: 'on_duty', validCredentialOnly: true } }), ...reason], visibleFor: { field: 'order_status', values: [16, 18] } },
|
||||
{ name: '开始罐装', resource: '/gasorder_basic/:identity/filling', fields: reason, visibleFor: { field: 'order_status', values: [18] } },
|
||||
{ name: '待配送', resource: '/gasorder_basic/:identity/ready', fields: reason, visibleFor: { field: 'order_status', values: [19] } },
|
||||
{ name: '开始配送', resource: '/gasorder_basic/:identity/delivering', fields: reason, visibleFor: { field: 'order_status', values: [20] } },
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--
|
||||
功能:承载资源详情页中的审核、流转、归属调整和菜单分配等短业务操作。
|
||||
版本:v1.0.0
|
||||
版本:v1.1.0
|
||||
-->
|
||||
<template>
|
||||
<a-modal
|
||||
@@ -21,11 +21,15 @@
|
||||
<a-alert v-if="isProductLifecycleAction" class="action-alert" type="info" show-icon>
|
||||
当前状态:{{ currentProductStatusLabel }}。请选择一个不同的目标状态,服务端会校验检修和订单占用情况。
|
||||
</a-alert>
|
||||
<a-alert v-if="isGasorderAssignmentAction" class="action-alert" type="info" show-icon>
|
||||
{{ assignmentScopeMessage }}
|
||||
</a-alert>
|
||||
<a-form :model="form" layout="vertical">
|
||||
<ResourceFieldForm
|
||||
v-model="form"
|
||||
:fields="actionFields"
|
||||
:required-keys="requiredKeys"
|
||||
:disabled-keys="disabledKeys"
|
||||
:relation-options="relations.options"
|
||||
:relation-loading="relations.loading"
|
||||
@change-relation="changeRelation"
|
||||
@@ -62,6 +66,30 @@ const relations = useResourceRelations();
|
||||
const isProductLifecycleAction = computed(
|
||||
() => props.action?.resource === '/product_info/:identity/lifecycle',
|
||||
);
|
||||
const isGasorderAssignmentAction = computed(
|
||||
() => props.action?.resource === '/gasorder_basic/:identity/assign',
|
||||
);
|
||||
/** 合同指定点始终锁定;无指定点的已分配订单也只能在当前点内改派。 */
|
||||
const lockedAssignmentDeliveryIdentity = computed(() => {
|
||||
return String(props.record.delivery_basic_identity ?? '').trim();
|
||||
});
|
||||
const disabledKeys = computed(() =>
|
||||
isGasorderAssignmentAction.value && lockedAssignmentDeliveryIdentity.value
|
||||
? ['delivery_basic_identity']
|
||||
: [],
|
||||
);
|
||||
const assignmentScopeMessage = computed(() => {
|
||||
if (
|
||||
Number(props.record.order_status) === 16 &&
|
||||
lockedAssignmentDeliveryIdentity.value
|
||||
) {
|
||||
return '配送合同已指定履约配送点,本次只能从该配送点的在岗且资质有效人员中分配。';
|
||||
}
|
||||
if (lockedAssignmentDeliveryIdentity.value) {
|
||||
return '订单已完成配送点分配,本次仅允许在当前配送点内改派配送人员。';
|
||||
}
|
||||
return '配送合同未指定配送点,请先从合同气站直属的启用配送点中选择,再选择该点配送人员。';
|
||||
});
|
||||
const actionFields = computed(() =>
|
||||
(props.action?.fields ?? []).map((field) =>
|
||||
isProductLifecycleAction.value && field.key === 'product_status'
|
||||
@@ -99,13 +127,52 @@ const ownershipKeys = [
|
||||
'user_account_identity',
|
||||
];
|
||||
|
||||
/** 业务动作字段不启用记录页联动,只沿用普通关联搜索。 */
|
||||
/** 为订单分配动作保留履约范围,其余业务动作沿用普通关联搜索。 */
|
||||
function searchRelation(field: ResourceField, keyword: string) {
|
||||
if (isGasorderAssignmentAction.value) {
|
||||
if (field.key === 'delivery_basic_identity') {
|
||||
relations.searchField(field, keyword, {
|
||||
filters: assignmentDeliveryFilters(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (field.key === 'staff_account_identity') {
|
||||
const deliveryIdentity = String(
|
||||
form.delivery_basic_identity ?? '',
|
||||
).trim();
|
||||
if (!deliveryIdentity) {
|
||||
relations.clear(field.relation);
|
||||
return;
|
||||
}
|
||||
relations.searchField(field, keyword, {
|
||||
filters: assignmentStaffFilters(deliveryIdentity),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
relations.searchField(field, keyword);
|
||||
}
|
||||
|
||||
/** 选择智能气阀的新归属时清空其余互斥归属,避免提交非法多重归属。 */
|
||||
/** 处理订单配送点级联及智能气阀互斥归属,避免提交已经失效的关联值。 */
|
||||
function changeRelation(field: ResourceField, value: unknown) {
|
||||
if (
|
||||
isGasorderAssignmentAction.value &&
|
||||
field.key === 'delivery_basic_identity'
|
||||
) {
|
||||
form.staff_account_identity = undefined;
|
||||
const deliveryIdentity = String(value ?? '').trim();
|
||||
const staffField = actionFields.value.find(
|
||||
(item) => item.key === 'staff_account_identity',
|
||||
);
|
||||
if (!staffField || !deliveryIdentity) {
|
||||
relations.clear('/staff_account');
|
||||
return;
|
||||
}
|
||||
void relations.loadField(staffField, '', {
|
||||
filters: assignmentStaffFilters(deliveryIdentity),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!isOwnershipAction.value ||
|
||||
!ownershipKeys.includes(field.key) ||
|
||||
@@ -119,6 +186,47 @@ function changeRelation(field: ResourceField, value: unknown) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 将订单履约气站转换为配送点候选接口的精确筛选参数。 */
|
||||
function assignmentDeliveryFilters(): Record<string, string> {
|
||||
const gasIdentity = String(props.record.gas_basic_identity ?? '').trim();
|
||||
return gasIdentity ? { gas_basic_identities: gasIdentity } : {};
|
||||
}
|
||||
|
||||
/** 配送人员必须同时属于订单气站和当前选择的配送点。 */
|
||||
function assignmentStaffFilters(deliveryIdentity: string) {
|
||||
const filters: Record<string, string> = {
|
||||
delivery_basic_identities: deliveryIdentity,
|
||||
};
|
||||
const gasIdentity = String(props.record.gas_basic_identity ?? '').trim();
|
||||
if (gasIdentity) filters.gas_basic_identities = gasIdentity;
|
||||
return filters;
|
||||
}
|
||||
|
||||
/** 按合同和订单快照初始化分配动作,禁止预加载全平台候选。 */
|
||||
async function initializeGasorderAssignment() {
|
||||
const deliveryField = actionFields.value.find(
|
||||
(field) => field.key === 'delivery_basic_identity',
|
||||
);
|
||||
const staffField = actionFields.value.find(
|
||||
(field) => field.key === 'staff_account_identity',
|
||||
);
|
||||
if (!deliveryField || !staffField) return;
|
||||
relations.clear('/delivery_basic');
|
||||
relations.clear('/staff_account');
|
||||
const lockedIdentity = lockedAssignmentDeliveryIdentity.value;
|
||||
if (lockedIdentity) {
|
||||
form.delivery_basic_identity = lockedIdentity;
|
||||
await relations.ensure('/delivery_basic', lockedIdentity);
|
||||
await relations.loadField(staffField, '', {
|
||||
filters: assignmentStaffFilters(lockedIdentity),
|
||||
});
|
||||
return;
|
||||
}
|
||||
await relations.loadField(deliveryField, '', {
|
||||
filters: assignmentDeliveryFilters(),
|
||||
});
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [props.visible, props.action?.name] as const,
|
||||
async ([visible]) => {
|
||||
@@ -128,6 +236,10 @@ watch(
|
||||
? props.record[field.key]
|
||||
: undefined;
|
||||
}
|
||||
if (isGasorderAssignmentAction.value) {
|
||||
await initializeGasorderAssignment();
|
||||
return;
|
||||
}
|
||||
await relations.preload(actionFields.value);
|
||||
if (props.action.resource.includes('/menu')) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user