采购价格管控

思路

(项目🔶-任务🟥-工作🔺)

  • 🔶通过下推财务应付控制采购价格。发票价格等于采购价格。如果不同需要修改采购订单

    • 🟥采购订单创建时 管理价格

      • 使用价目表
      • 没有价目表 单价可修改
    • 🟥应付单时管理价格 (单据页)

      • 保存后可获取采购订单价格

        • 🔺制作 单据页获取价格按钮
          • 🔺 捕获事件 执行修改数据并刷新
      • 🔺价格和应付价格不相等可以保存,但是不能提交

        • 🔺通过插件拦截提交,除非价格和应付一样
    • 🟥应付单时管理价格 (单据页)

      • 批量获取

建议用插件实现,不用实体规则


目标通过 对指定物料开启 价格管控来

  • 确保发票价格=采购价格

首先选出2个物料代码,一个启用价格管控(3.02.01.0008),一个没有启用价格管控(3.99.01.0035)

有管控物料编码

3.02.01.0008
  • 带管控
  • 有价目表


无管控物料编码
3.99.01.0035
  • 不管控
  • 无价目表


价目表

被管控物料 必须有价目表

采购下单

可手动修改


自动获取价目表 (无法修改)

价格变更

参数【已关闭订单可以变更】未勾选,订单已关闭不能变更。

【 供应链 】->【 采购管理】->【采购管理系统参数】

临时改价

通过新变更单

  • 新变更单要求能改价格

永久改价

通过价目表


应付对价


bos设置




插件模式

通过列表插件 / 表单插件来完成。

菜单点击事件

通过插件来处理数据建立以一个无操作按钮

通过表单点击菜单 判断点击按钮表示 如: “XJPT_tbButton_2” 来执行内容

简单处理

import clr

#添加对cloud插件开发的常用组件的引用
clr.AddReference('System')
clr.AddReference('System.Data')
clr.AddReference('Kingdee.BOS')
clr.AddReference('Kingdee.BOS.Core')
clr.AddReference('Kingdee.BOS.App')
clr.AddReference('Kingdee.BOS.ServiceHelper')
#clr.AddReference('Kingdee.BOS.KDSReportEntity')
#clr.AddReference('Kingdee.BOS.App.KDSService')
clr.AddReference('Newtonsoft.Json')


def BarItemClick(e):
    key=e.BarItemKey;
    this.View.ShowMessage("欢迎回来 {0} ".format (key));
 

修改数据并刷新

import clr
import sys
clr.AddReference('System')
clr.AddReference('Kingdee.BOS.Core')
clr.AddReference('Newtonsoft.Json')
clr.AddReference('Kingdee.BOS.App')
from Kingdee.BOS import *
from Kingdee.BOS.Core import *
from Kingdee.BOS.Core.Bill import *
from Kingdee.BOS.Core.DynamicForm.PlugIn import *
from Kingdee.BOS.Core.DynamicForm.PlugIn.ControlModel import *
from Kingdee.BOS.JSON import * 
from Newtonsoft.Json import JsonConvert

from System import *
import System
import System.IO
from Kingdee.BOS.App.Data import *

def getDt():
    """  获取事件 """
    current_date = System.DateTime.Now
    date_string = current_date.ToString("MM-dd HH:mm:ss")
    return date_string
    
def myLog(msg):
    """ 写日志到文件 """
    current_date = System.DateTime.Now
    # 格式化输出(例如:2023-11-20)
    date_string = current_date.ToString("yyyy-MM-dd")
    logWriter = System.IO.StreamWriter("d:\jack\debug_output-{}.txt".format(date_string), True)
    # 重定向到文件
    original_out = System.Console.Out
    System.Console.SetOut(logWriter)
    # 4. 使用Console.WriteLine输出(会被重定向到文件)
    System.Console.WriteLine(msg)
    # 恢复并关闭
    System.Console.SetOut(original_out)
    logWriter.Close()

def showObj(obj):
    """ 显示对象内容  """
    
    myLog("{0} {1} dir( obj ) {1} {2}\t".format(getDt(),  '-'*10,  type(obj).__name__))
    for i in dir(obj):
        try:
            cobj = getattr(obj,i,None)
        except Exception as e:
            myLog("Error: {}".format(e))
        myLog("  {0} , type = {1} value = {2} ".format(i ,  type(cobj).__name__, cobj      ))
      


def BarItemClick(e):
    key=e.BarItemKey;
    if(key !="XJPT_tbButton_2" ): 
        return 
    
    showObj(e)
    showObj(this.View.Model.DataObject)

    iD = this.View.Model.DataObject.Id  
    if(iD <= 0  ): 
        this.View.ShowMessage("未保存单据不能获取 {} ".format (  ''  ));
    else:
        fsql="/*dialect*/ exec z_fxfph  {0}".format(iD);
        DBUtils.Execute(this.Context,fsql);

    this.View.ShowMessage("完成操作 {0} ".format ( "" ));
    this.View.InvokeFormOperation("Refresh")
    
    
    myLog("  this.View.Model.DataObject.Id =={0} ".format(this.View.Model.DataObject.Id      ))
    
def OnLoad(e):
    return
    #---------
    myLog("{0}OnLoad e {0} {1}\t".format('-'*10, type(e).__name__))
    for i in dir(e):
        myLog("\t{}".format(i))

提交判断提示

表单数据路径 this.View.Model.DataObject.表单名ENTRY ,其中AP_PAYABLE应付单

this.View.Model.DataObject.AP_PAYABLEENTRY 

导出数据模型

文件》数据模型》查找》导出 ,参考表格中的实体属性一列 ,非表格字段标识

采购订单含税单价==含税单价  and 采购订单单价==单价
F_XJPT_Price==TaxPrice and F_XJPT_Price1==FPrice
import clr
import sys
clr.AddReference('System')
clr.AddReference('Kingdee.BOS.Core')
clr.AddReference('Newtonsoft.Json')
clr.AddReference('Kingdee.BOS.App')
from Kingdee.BOS import *
from Kingdee.BOS.Core import *
from Kingdee.BOS.Core.Bill import *
from Kingdee.BOS.Core.DynamicForm.PlugIn import *
from Kingdee.BOS.Core.DynamicForm.PlugIn.ControlModel import *
from Kingdee.BOS.JSON import * 
from Newtonsoft.Json import JsonConvert

from System import *
import System
import System.IO
from Kingdee.BOS.App.Data import *

pName = "保存和提交时检查价格是否相同"

def getDt():
    """  获取时间 """
    current_date = System.DateTime.Now
    date_string = current_date.ToString("MM-dd HH:mm:ss")
    return date_string
    
def myLog(msg):
    """ 写日志到文件 """
    current_date = System.DateTime.Now
    # 格式化输出(例如:2023-11-20)
    date_string = current_date.ToString("yyyy-MM-dd")
    logWriter = System.IO.StreamWriter("d:\jack\debug_output-{}.txt".format(date_string), True)
    # 重定向到文件
    original_out = System.Console.Out
    System.Console.SetOut(logWriter)
    # 4. 使用Console.WriteLine输出(会被重定向到文件)
    System.Console.WriteLine(msg)
    # 恢复并关闭
    System.Console.SetOut(original_out)
    logWriter.Close()

def showObj(obj):
    """ 显示对象内容 到log文件  """
    
    myLog("{0} {1} dir( obj ) {1} {2}\t".format(getDt(),  '-'*10,  type(obj).__name__))
    for i in dir(obj):
        try:
            cobj = getattr(obj,i,None)
        except Exception as e:
            myLog("Error: {}".format(e))
        myLog("  {0} , type = {1} value = {2} ".format(i ,  type(cobj).__name__, cobj      ))
      
#-----------------
def BeforeDoOperation(e):
    """ 捕获所以操作事件 """
    showObj(e)
    tmp = "myLog:str(e.Operation) {0} , type = {1} value = {2} "
    tmp = tmp.format(str(e.Operation) ,  type(e.Operation).__name__ ,  e.Operation    )
    myLog(tmp)
    # 判断是否为提交操作
    if  type(e.Operation).__name__ == "Submit":
        提交操作(e)
        return 

    
def 提交操作(e):
    """  提交操作  """
    #this.View.Model.DataObject
    #showObj(this.View.Model.DataObject)
    entry_list = this.View.Model.DataObject.AP_PAYABLEENTRY
    for ir in entry_list:
        # 打印条目
        #msg = "{}\n".format(JsonConvert.SerializeObject( ir )) 
        #myLog(msg)

        tmp = "myLog:F_XJPT_Price= {0} , FTaxPrice = {1}  F_XJPT_Price1 = {2}  FPrice = {3}"
        tmp = tmp.format( ir["F_XJPT_Price"] ,  ir["TaxPrice"] , ir["F_XJPT_Price1"] ,   ir["FPrice"] )
        myLog(tmp)
    
        if ir.F_XJPT_Price==ir.TaxPrice and ir.F_XJPT_Price1==ir.FPrice:
        # 采购订单含税单价==含税单价  and 采购订单单价==单价
            return 
    # 禁止提交
    e.Cancel=True # 取消当前操作
    raise Exception("采购价格和应付价格不同,请上下查单据的价格!")
    #key=e.BarItemKey;
    #this.View.ShowMessage("完成操作 {0} ".format ( 123 ));

def BeforeSave(e):
    showObj(e)
    #e.Cancel=True # 这个怎么写 
    #raise Exception("采购价格和应付价格不同,请上下查单据的价格!")
    #key=e.BarItemKey;
    #this.View.ShowMessage("完成操作 {0} ".format ( 123 ));
     

通用事件处理

import clr
import sys
clr.AddReference('System')
clr.AddReference('Kingdee.BOS.Core')
clr.AddReference('Newtonsoft.Json')
clr.AddReference('Kingdee.BOS.App')
from Kingdee.BOS import *
from Kingdee.BOS.Core import *
from Kingdee.BOS.Core.Bill import *
from Kingdee.BOS.Core.DynamicForm.PlugIn import *
from Kingdee.BOS.Core.DynamicForm.PlugIn.ControlModel import *
from Kingdee.BOS.JSON import * 
from Newtonsoft.Json import JsonConvert

from System import *
import System
import System.IO
from Kingdee.BOS.App.Data import *

def getDt():
    """  获取事件 """
    current_date = System.DateTime.Now
    date_string = current_date.ToString("MM-dd HH:mm:ss")
    return date_string
    
def myLog(msg):
    """ 写日志到文件 """
    current_date = System.DateTime.Now
    # 格式化输出(例如:2023-11-20)
    date_string = current_date.ToString("yyyy-MM-dd")
    logWriter = System.IO.StreamWriter("d:\jack\debug_output-{}.txt".format(date_string), True)
    # 重定向到文件
    original_out = System.Console.Out
    System.Console.SetOut(logWriter)
    # 4. 使用Console.WriteLine输出(会被重定向到文件)
    System.Console.WriteLine(msg)
    # 恢复并关闭
    System.Console.SetOut(original_out)
    logWriter.Close()

def showObj(obj):
    """ 显示对象内容  """
    
    myLog("{0} {1} dir( obj ) {1} {2}\t".format(getDt(),  '-'*10,  type(obj).__name__))
    for i in dir(obj):
        try:
            cobj = getattr(obj,i,None)
        except Exception as e:
            myLog("Error: {}".format(e))
        myLog("  {0} , type = {1} value = {2} ".format(i ,  type(cobj).__name__, cobj      ))
      
#-----------------
def BeforeDoOperation(e):
    showObj(e)
    
    myLog("myLog:str(e.Operation) {0} , type = {1} value = {2} ".format(str(e.Operation),  type(e.Operation).__name__, e.Operation    ))
    # 判断是否为提交操作
    if  type(e.Operation).__name__ != "Submit":
        return 
    
    
    e.Cancel=True # 这个怎么写 
    raise Exception("采购价格和应付价格不同,请上下查单据的价格!")
    #key=e.BarItemKey;
    #this.View.ShowMessage("完成操作 {0} ".format ( 123 ));
    
def BeforeSave(e):
    showObj(e)
    #e.Cancel=True # 这个怎么写 
    #raise Exception("采购价格和应付价格不同,请上下查单据的价格!")
    #key=e.BarItemKey;
    #this.View.ShowMessage("完成操作 {0} ".format ( 123 ));
     

空操作技术(不推荐)

  • 配置前端点击事件
  • 通过点击事件空操作+python脚本
  • 调用存储过程更新数据

添加菜单

工具条右击->按钮->单击事件

  • 新增/修改【服务编辑】->改服务描述->新增

    • 新增/修改【操作编辑】->操作类型=空操作->修改操作代码->修改名称
    • 其他控制->服务插件
      • 注册脚本->编辑脚本

这种方法过于麻烦不如 插件模式



alter PROCEDURE [dbo].[z_fxfph]--exec z_fxfph '100039'
@fid int
AS
BEGIN

update b set b.F_XJPT_PRICE1=g.FPRICE,b.F_XJPT_PRICE=g.FTAXPRICE
from T_AP_PAYABLE a 
join T_AP_PAYABLEENTRY b on b.fid=a.fid
join T_AP_PAYABLE_LK c on c.fentryid=b.fentryid
join T_AP_PAYABLEENTRY i on i.fentryid=c.fsid
join T_AP_PAYABLE j on j.fid=c.fsbillid
join T_AP_PAYABLE_LK k on k.fentryid=i.fentryid 
join T_STK_INSTOCKENTRY d on d.fentryid=k.fsid
join T_STK_INSTOCK e on e.fid=k.fsbillid 
join T_STK_INSTOCKENTRY_LK f on f.fentryid=d.fentryid --and a.fbillno='AP00026516'
join T_PUR_POORDERENTRY_F  g on g.fentryid=f.fsid
join T_PUR_POORDER h on h.fid=f.fsbillid 
where a.FSETACCOUNTTYPE=3  and a.fid=@fid

end


exec z_fxfph '100039'
# 引入CLR运行库
import clr
# 添加插件开发必要组件
clr.AddReference('Kingdee.BOS')
clr.AddReference('Kingdee.BOS.Core')
clr.AddReference('Kingdee.BOS.App')
# 导入命名空间
from Kingdee.BOS import *
from Kingdee.BOS.Core import *
from Kingdee.BOS.Core.Bill import *
from Kingdee.BOS.Core.DynamicForm.PlugIn import *
from Kingdee.BOS.Core.DynamicForm.PlugIn.ControlModel import *
from System import *
from Kingdee.BOS.App.Data import *

def AfterExecuteOperationTransaction(e):
    try:
        # 确保操作对象有效
        if e.DataEntitys is None or e.DataEntitys.Count == 0:
            this.View.ShowMessage("未获取到有效数据!")
            return

        # 遍历数据实体
        for item in e.DataEntitys:
            # 获取主键ID,注意字段名区分大小写(通常为"Id")
            fid = str(item["Id"]) if "Id" in item and item["Id"] else None
            if not fid:
                this.View.ShowMessage("主键ID为空,无法执行操作!")
                continue  # 跳过当前项

            # 方案1:安全参数化查询(推荐,若DBUtils支持)
            # fsql = "/*dialect*/ EXEC z_fxfph @FID=?"
            # params = (fid,)
            # DBUtils.Execute(this.Context, fsql, params)

            # 方案2:直接拼接(需防注入,确保fid安全)
            fsql = "/*dialect*/ EXEC z_fxfph @FID='{}'".format(fid)
            DBUtils.Execute(this.Context, fsql)

            # 可选:记录执行日志
            this.Logger.Info("成功执行存储过程,单据ID: {}".format(fid))

    except Exception as ex:
        # 异常处理
        this.View.ShowMessage("执行出错: " + str(ex))
        this.Logger.Error("插件执行错误", ex)
    return

提醒

合法性检查

提交时检查

保存操纵调用

条件

  FPrice  !=    F_XJPT_Price1  OR   FPriceWithTax  != F_XJPT_Price