Compare commits
16 Commits
feature/67
...
feature/71
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f000a6be4 | ||
|
|
376eb9e56f | ||
|
|
1dfa22900d | ||
|
|
0eeebf437a | ||
|
|
397d4f29a1 | ||
|
|
33205c5d29 | ||
|
|
568f3e4f30 | ||
|
|
8c43fc2b76 | ||
|
|
8960d3d07c | ||
|
|
95b5c86242 | ||
|
|
c2000aa9c5 | ||
|
|
e29456bbf7 | ||
|
|
a2f8dc6cec | ||
|
|
fec095ca6b | ||
|
|
aed33dbb35 | ||
|
|
dbe8c95558 |
@@ -6,3 +6,4 @@ from . import mrp_production
|
|||||||
from . import purchase_order
|
from . import purchase_order
|
||||||
from . import stock_rule
|
from . import stock_rule
|
||||||
from . import stock_picking
|
from . import stock_picking
|
||||||
|
from . import product_product
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ class MrpProduction(models.Model):
|
|||||||
if item.product_id.is_customer_provided:
|
if item.product_id.is_customer_provided:
|
||||||
item.pr_mp_count = 0
|
item.pr_mp_count = 0
|
||||||
else:
|
else:
|
||||||
# 由于采购申请合并了所有销售订单行的采购,所以不区分产品
|
pr_ids = item._get_purchase_request()
|
||||||
mrp_names = self.env['mrp.production'].search([('origin', '=', item.origin)]).mapped('name')
|
|
||||||
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'in', mrp_names)])
|
|
||||||
item.pr_mp_count = len(pr_ids)
|
item.pr_mp_count = len(pr_ids)
|
||||||
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')])
|
# pr_ids = self.env['purchase.request'].sudo().search([('origin', 'like', item.name), ('is_subcontract', '!=', 'True')])
|
||||||
|
|
||||||
@@ -25,8 +23,7 @@ class MrpProduction(models.Model):
|
|||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
|
|
||||||
# 由于采购申请合并了所有销售订单行的采购,所以不区分产品
|
# 由于采购申请合并了所有销售订单行的采购,所以不区分产品
|
||||||
mrp_names = self.env['mrp.production'].search([('origin', '=', self.origin)]).mapped('name')
|
pr_ids = self._get_purchase_request()
|
||||||
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'in', mrp_names)])
|
|
||||||
|
|
||||||
action = {
|
action = {
|
||||||
'res_model': 'purchase.request',
|
'res_model': 'purchase.request',
|
||||||
@@ -44,3 +41,12 @@ class MrpProduction(models.Model):
|
|||||||
'view_mode': 'tree,form',
|
'view_mode': 'tree,form',
|
||||||
})
|
})
|
||||||
return action
|
return action
|
||||||
|
|
||||||
|
def _get_purchase_request(self):
|
||||||
|
"""获取跟制造订单相关的采购申请单(根据采购申请单行项目的产品匹配)"""
|
||||||
|
mrp_names = self.env['mrp.production'].search([('origin', '=', self.origin)]).mapped('name')
|
||||||
|
pr_ids = self.env['purchase.request'].sudo().search([('origin', 'in', mrp_names)])
|
||||||
|
product_list = self.product_id._get_product_include_bom()
|
||||||
|
pr_line_ids = pr_ids.line_ids.filtered(lambda l: l.product_id in product_list)
|
||||||
|
return pr_line_ids.mapped('request_id')
|
||||||
|
|
||||||
17
jikimo_purchase_request/models/product_product.py
Normal file
17
jikimo_purchase_request/models/product_product.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from odoo import models
|
||||||
|
|
||||||
|
|
||||||
|
class ProductProduct(models.Model):
|
||||||
|
_inherit = 'product.product'
|
||||||
|
|
||||||
|
|
||||||
|
def _get_product_include_bom(self):
|
||||||
|
"""获取产品列表(包括所有bom)"""
|
||||||
|
self.ensure_one()
|
||||||
|
product_list = [self]
|
||||||
|
bom_ids = self.bom_ids
|
||||||
|
while (bom_ids):
|
||||||
|
bom_product_ids = bom_ids.bom_line_ids.mapped('product_id')
|
||||||
|
product_list.append(bom_product_ids)
|
||||||
|
bom_ids = bom_product_ids.bom_ids
|
||||||
|
return product_list
|
||||||
@@ -31,20 +31,12 @@ class PurchaseOrder(models.Model):
|
|||||||
|
|
||||||
def button_cancel(self):
|
def button_cancel(self):
|
||||||
"""
|
"""
|
||||||
1. 先将采购订单行与目标库存移动断开链接,避免采购单取消后,调拨单被调整为mts的问题
|
将取消的采购订单关联的库存移动撤销
|
||||||
2. 取消采购订单
|
|
||||||
3. 将采购订单行与目标库存移动重新建立链接
|
|
||||||
"""
|
"""
|
||||||
created_purchase_request_line_ids = {}
|
move_ids = self.order_line.move_dest_ids.filtered(lambda move: move.state != 'done' and not move.scrapped)
|
||||||
if self.order_line.move_dest_ids.created_purchase_request_line_id:
|
|
||||||
move_ids = self.order_line.move_dest_ids.filtered(lambda move: move.state != 'done' and not move.scrapped)
|
|
||||||
created_purchase_request_line_ids = {move.id: move.created_purchase_request_line_id for move in move_ids}
|
|
||||||
self.order_line.write({'move_dest_ids': [(5, 0, 0)]})
|
|
||||||
res =super(PurchaseOrder, self).button_cancel()
|
res =super(PurchaseOrder, self).button_cancel()
|
||||||
for move_id, created_purchase_request_line_id in created_purchase_request_line_ids.items():
|
if move_ids.mapped('created_purchase_request_line_id'):
|
||||||
self.env['stock.move'].browse(move_id).created_purchase_request_line_id = created_purchase_request_line_id
|
move_ids.write({'state': 'waiting', 'is_done': False})
|
||||||
# if move_ids.mapped('created_purchase_request_line_id'):
|
|
||||||
# move_ids.write({'state': 'waiting', 'is_done': False})
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def write(self, vals):
|
def write(self, vals):
|
||||||
|
|||||||
@@ -42,6 +42,6 @@ class StockPicking(models.Model):
|
|||||||
purchase_request_lines = self.move_ids.move_orig_ids.purchase_line_id.purchase_request_lines
|
purchase_request_lines = self.move_ids.move_orig_ids.purchase_line_id.purchase_request_lines
|
||||||
if purchase_request_lines:
|
if purchase_request_lines:
|
||||||
purchase_request_lines.move_dest_ids = [
|
purchase_request_lines.move_dest_ids = [
|
||||||
(4, x.id) for x in backorder_ids.move_ids if x.product_id.id == purchase_request_lines.product_id.id
|
(4, x.id) for x in backorder_ids.move_ids if x.product_id.id in purchase_request_lines.mapped('product_id.id')
|
||||||
]
|
]
|
||||||
return res
|
return res
|
||||||
@@ -23,6 +23,7 @@
|
|||||||
],
|
],
|
||||||
'web.assets_backend': [
|
'web.assets_backend': [
|
||||||
'sf_demand_plan/static/src/scss/style.css',
|
'sf_demand_plan/static/src/scss/style.css',
|
||||||
|
'sf_demand_plan/static/src/js/print_demand.js',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
'license': 'LGPL-3',
|
'license': 'LGPL-3',
|
||||||
|
|||||||
@@ -272,18 +272,15 @@ class SfProductionDemandPlan(models.Model):
|
|||||||
else:
|
else:
|
||||||
record.actual_end_date = None
|
record.actual_end_date = None
|
||||||
|
|
||||||
@api.depends('sale_order_id.mrp_production_ids.move_raw_ids.forecast_availability',
|
@api.depends('sale_order_id.mrp_production_ids.move_raw_ids.reserved_availability')
|
||||||
'sale_order_id.mrp_production_ids.move_raw_ids.quantity_done')
|
|
||||||
def _compute_material_check(self):
|
def _compute_material_check(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
if record.sale_order_id and record.sale_order_id.mrp_production_ids:
|
if record.sale_order_id and record.sale_order_id.mrp_production_ids:
|
||||||
manufacturing_orders = record.sale_order_id.mrp_production_ids.filtered(
|
manufacturing_orders = record.sale_order_id.mrp_production_ids.filtered(
|
||||||
lambda mo: mo.product_id == record.product_id)
|
lambda mo: mo.product_id == record.product_id)
|
||||||
if manufacturing_orders and manufacturing_orders.move_raw_ids:
|
if manufacturing_orders and manufacturing_orders.move_raw_ids:
|
||||||
total_forecast_availability = sum(manufacturing_orders.mapped('move_raw_ids.forecast_availability'))
|
total_reserved_availability = sum(manufacturing_orders.mapped('move_raw_ids.reserved_availability'))
|
||||||
total_quantity_done = sum(manufacturing_orders.mapped('move_raw_ids.quantity_done'))
|
if float_compare(total_reserved_availability, record.product_uom_qty,
|
||||||
total_sum = total_forecast_availability + total_quantity_done
|
|
||||||
if float_compare(total_sum, record.product_uom_qty,
|
|
||||||
precision_rounding=record.product_id.uom_id.rounding) >= 0:
|
precision_rounding=record.product_id.uom_id.rounding) >= 0:
|
||||||
record.material_check = '1' # 已齐套
|
record.material_check = '1' # 已齐套
|
||||||
else:
|
else:
|
||||||
@@ -323,6 +320,7 @@ class SfProductionDemandPlan(models.Model):
|
|||||||
'name': _("打印"),
|
'name': _("打印"),
|
||||||
'domain': [('demand_plan_id', 'in', self.ids)],
|
'domain': [('demand_plan_id', 'in', self.ids)],
|
||||||
'views': [[self.env.ref('sf_demand_plan.action_plan_print_tree').id, 'list']],
|
'views': [[self.env.ref('sf_demand_plan.action_plan_print_tree').id, 'list']],
|
||||||
|
'search_view_id': self.env.ref('sf_demand_plan.action_plan_print_search').id,
|
||||||
'target': 'new',
|
'target': 'new',
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -503,7 +501,10 @@ class SfProductionDemandPlan(models.Model):
|
|||||||
action = self.env["ir.actions.actions"]._for_xml_id("stock.action_picking_tree_all")
|
action = self.env["ir.actions.actions"]._for_xml_id("stock.action_picking_tree_all")
|
||||||
picking_ids = None
|
picking_ids = None
|
||||||
if self.supply_method in ('automation', 'manual'):
|
if self.supply_method in ('automation', 'manual'):
|
||||||
picking_ids = self.sale_order_id.mrp_production_ids.mapped('picking_ids').filtered(
|
mrp_production_ids = self.sale_order_id.mrp_production_ids.filtered(
|
||||||
|
lambda p: p.product_id.id == self.product_id.id
|
||||||
|
)
|
||||||
|
picking_ids = mrp_production_ids.mapped('picking_ids').filtered(
|
||||||
lambda p: p.state == 'assigned')
|
lambda p: p.state == 'assigned')
|
||||||
elif self.supply_method in ('purchase', 'outsourcing'):
|
elif self.supply_method in ('purchase', 'outsourcing'):
|
||||||
picking_ids = self.sale_order_id.picking_ids.filtered(
|
picking_ids = self.sale_order_id.picking_ids.filtered(
|
||||||
|
|||||||
215
sf_demand_plan/static/src/js/print_demand.js
Normal file
215
sf_demand_plan/static/src/js/print_demand.js
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
odoo.define('sf_demand.print_demand', function (require) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var ListController = require('web.ListController');
|
||||||
|
var ListRenderer = require('web.ListRenderer');
|
||||||
|
var ListView = require('web.ListView');
|
||||||
|
var viewRegistry = require('web.view_registry');
|
||||||
|
var { url } = require("@web/core/utils/urls")
|
||||||
|
|
||||||
|
var CustomListRenderer = ListRenderer.extend({
|
||||||
|
_render: function () {
|
||||||
|
var self = this;
|
||||||
|
this.getParent()?.$buttons.hide();
|
||||||
|
|
||||||
|
return this._super.apply(this, arguments).then(function () {
|
||||||
|
// 添加图片预览容器到页面左侧
|
||||||
|
if (!$('.table-image-preview-container').length) {
|
||||||
|
self.$el.parent().addClass('custom-table-image-container')
|
||||||
|
self.$el.before(
|
||||||
|
`<div class="custom-preview-container">
|
||||||
|
<img class="table-image-preview-container" src="" />
|
||||||
|
<iframe class="table-image-preview-container" src=""/>
|
||||||
|
</div>`
|
||||||
|
);
|
||||||
|
self.$el.prepend(`
|
||||||
|
<div class="print-button-container" style="margin-bottom:10px;">
|
||||||
|
<button class="btn btn-primary o_print_custom">
|
||||||
|
<i class="fa fa-print"></i> 打印
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-secondary o_cancel_custom">
|
||||||
|
取消
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
start: function() {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$el.find('.o_data_row').eq(0).trigger('click')
|
||||||
|
}, 500);
|
||||||
|
return this._super();
|
||||||
|
},
|
||||||
|
events: _.extend({}, ListRenderer.prototype.events, {
|
||||||
|
'click .o_data_row': '_onCustomRowClick',
|
||||||
|
'click .o_print_custom': '_onPrintClick',
|
||||||
|
'click .o_cancel_custom': '_onCancelClick'
|
||||||
|
}),
|
||||||
|
_onCancelClick() {
|
||||||
|
this.getParent()?.getParent()?.dialogs.closeAll()
|
||||||
|
},
|
||||||
|
_onCustomRowClick: async function (ev) {
|
||||||
|
var self = this;
|
||||||
|
var $row = $(ev.currentTarget);
|
||||||
|
var index = $row.index();
|
||||||
|
var data = this.state.data[index];
|
||||||
|
if(data.fileData?.fileUrl) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
data.fileData = { }
|
||||||
|
if(data.res_id) {
|
||||||
|
// 正确获取 ORM 服务的方式
|
||||||
|
// var orm = this.getParent().getParent().env.services
|
||||||
|
const key = data.data.type == 1 ? 'machining_drawings' : 'cnc_worksheet'
|
||||||
|
const attachment = await this._rpc({
|
||||||
|
model: 'ir.binary',
|
||||||
|
method: 'attachment_info',
|
||||||
|
args: [
|
||||||
|
data.model,
|
||||||
|
data.res_id,
|
||||||
|
key
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
if (attachment) {
|
||||||
|
Object.assign(data.fileData, attachment)
|
||||||
|
this._getTypeInfo(attachment.mimetype, data.fileData)
|
||||||
|
}
|
||||||
|
const fileUrl = this.getFileUrl(data.fileData.attachment_type, data, key)
|
||||||
|
data.fileData.fileUrl = fileUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$('.table-image-preview-container').hide()
|
||||||
|
if(data.fileData.attachment_type == 'iframe') {
|
||||||
|
|
||||||
|
$('iframe.table-image-preview-container').attr('src', decodeURIComponent(data.fileData.fileUrl) ).show()
|
||||||
|
} else {
|
||||||
|
$('img.table-image-preview-container').attr('src', data.fileData.fileUrl).show()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getSelectedIds: function() {
|
||||||
|
return this.state.data.map(_ => {
|
||||||
|
return _.data.id
|
||||||
|
})
|
||||||
|
},
|
||||||
|
_onPrintClick(e) {
|
||||||
|
var print_ids = this.getSelectedIds();
|
||||||
|
|
||||||
|
|
||||||
|
this._rpc({
|
||||||
|
model: 'sf.demand.plan.print.wizard',
|
||||||
|
method: 'demand_plan_print',
|
||||||
|
args: [ print_ids ] ,
|
||||||
|
// context: this.state.getContext()
|
||||||
|
}).then((e) => {
|
||||||
|
|
||||||
|
this.getParent()?.getParent()?.env.services?.notification.notify( {
|
||||||
|
type: 'info',
|
||||||
|
message: e.message,
|
||||||
|
})
|
||||||
|
// self.do_notify("成功", "打印任务已发送到打印机");
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.error("打印错误:", error);
|
||||||
|
// self.do_warn("打印失败", error.data.message || "未知错误");
|
||||||
|
}).finally(e => {
|
||||||
|
this.getParent().reload()
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
},
|
||||||
|
getFileUrl(attachment_type, data, key) {
|
||||||
|
let fileUrl
|
||||||
|
switch (attachment_type) {
|
||||||
|
case 'image':
|
||||||
|
const timer = +new Date()
|
||||||
|
fileUrl = url("/web/image", {
|
||||||
|
model: data.model,
|
||||||
|
id: data.res_id,
|
||||||
|
field: key,
|
||||||
|
unique: '',
|
||||||
|
timer
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'iframe':
|
||||||
|
|
||||||
|
const iframe_file_url = encodeURIComponent(
|
||||||
|
url("/web/content", {
|
||||||
|
model: data.model,
|
||||||
|
id: data.res_id,
|
||||||
|
field: key,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
fileUrl = `${this.state.attachment_base}?file=${iframe_file_url}`;
|
||||||
|
case 'unknown':
|
||||||
|
fileUrl = encodeURIComponent(
|
||||||
|
url("/web/content", {
|
||||||
|
model: data.model,
|
||||||
|
id: data.res_id,
|
||||||
|
field: key,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileUrl
|
||||||
|
},
|
||||||
|
_getTypeInfo(type, data) {
|
||||||
|
switch (type) {
|
||||||
|
case 'application/pdf':
|
||||||
|
data.attachment_base = `/web/static/lib/pdfjs/web/viewer.html`;
|
||||||
|
data.attachment_type = 'iframe'
|
||||||
|
break;
|
||||||
|
case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
|
||||||
|
data.attachment_base = `/jikimo_attachment_viewer/static/lib/docxjs/viewer.html`;
|
||||||
|
data.attachment_type = 'iframe'
|
||||||
|
break;
|
||||||
|
case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
|
||||||
|
data.attachment_base = `/jikimo_attachment_viewer/static/lib/exceljs/viewer.html`;
|
||||||
|
data.attachment_type = 'iframe'
|
||||||
|
break;
|
||||||
|
case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':
|
||||||
|
data.attachment_base = '';
|
||||||
|
data.attachment_type = 'unknown'
|
||||||
|
break;
|
||||||
|
case 'image/png':
|
||||||
|
case 'image/jpeg':
|
||||||
|
case 'image/jpg':
|
||||||
|
data.attachment_base = ''
|
||||||
|
data.attachment_type = 'image'
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
data.attachment_base = ''
|
||||||
|
data.attachment_type = 'unknown'
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var CustomListController = ListController.extend({
|
||||||
|
// 可以保留或移除,根据是否需要处理自定义事件
|
||||||
|
// 处理打印操作
|
||||||
|
// rpc.query({
|
||||||
|
// model: 'sf.demand.plan.print.wizard',
|
||||||
|
// method: 'demand_plan_print',
|
||||||
|
// args: [recordIds]
|
||||||
|
// }).then(function(result) {
|
||||||
|
// self.do_notify("成功", "打印任务已发送到打印机");
|
||||||
|
// // 刷新视图显示最新状态
|
||||||
|
// self.reload();
|
||||||
|
// }).catch(function(error) {
|
||||||
|
// self.do_warn("打印失败", error.data.message || "发生未知错误");
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
||||||
|
var PrintDemand = ListView.extend({
|
||||||
|
config: _.extend({}, ListView.prototype.config, {
|
||||||
|
Renderer: CustomListRenderer,
|
||||||
|
Controller: CustomListController,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
viewRegistry.add('print_demand', PrintDemand);
|
||||||
|
|
||||||
|
return PrintDemand;
|
||||||
|
});
|
||||||
@@ -9,3 +9,58 @@
|
|||||||
.demand_plan_tree .o_list_table_ungrouped {
|
.demand_plan_tree .o_list_table_ungrouped {
|
||||||
min-width: 1900px;
|
min-width: 1900px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.o_selected_row {
|
||||||
|
background-color: #e6f7ff !important;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-table-image-container {
|
||||||
|
display: flex;
|
||||||
|
height: calc(95vh - 254px);
|
||||||
|
gap: 20px;
|
||||||
|
position: relative;
|
||||||
|
th.o_list_record_selector, td.o_list_record_selector{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.custom-preview-container, .print_demand {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
max-width: 49%;
|
||||||
|
tbody {
|
||||||
|
tr:not(.o_data_row) {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tfoot {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.print_demand {
|
||||||
|
.table-responsive {
|
||||||
|
width: 100%;
|
||||||
|
overflow-x: auto!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.custom-preview-container {
|
||||||
|
background-color: #dadce0;
|
||||||
|
padding: 20px;
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.o_print_custom, .o_cancel_custom {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
|
.o_print_custom {
|
||||||
|
right: 66px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,8 +30,15 @@ class SfDemandPlanPrintWizard(models.TransientModel):
|
|||||||
workorder_id = fields.Many2one('mrp.workorder', string='工单')
|
workorder_id = fields.Many2one('mrp.workorder', string='工单')
|
||||||
cnc_worksheet = fields.Binary('程序单')
|
cnc_worksheet = fields.Binary('程序单')
|
||||||
|
|
||||||
def demand_plan_print(self):
|
@api.model
|
||||||
for record in self:
|
def demand_plan_print(self, print_ids):
|
||||||
|
plan_print_ids = self.env['sf.demand.plan.print.wizard'].sudo().search(
|
||||||
|
[('id', 'in', print_ids)])
|
||||||
|
if not plan_print_ids:
|
||||||
|
return {'message': '记录不存在'}
|
||||||
|
success_records = []
|
||||||
|
failed_records = []
|
||||||
|
for record in plan_print_ids:
|
||||||
pdf_data = record.machining_drawings if record.type == '1' else record.cnc_worksheet
|
pdf_data = record.machining_drawings if record.type == '1' else record.cnc_worksheet
|
||||||
if pdf_data:
|
if pdf_data:
|
||||||
try:
|
try:
|
||||||
@@ -46,9 +53,20 @@ class SfDemandPlanPrintWizard(models.TransientModel):
|
|||||||
elif record.type == '2':
|
elif record.type == '2':
|
||||||
c_num += 1
|
c_num += 1
|
||||||
record.demand_plan_id.print_count = f"T{t_num}C{c_num}"
|
record.demand_plan_id.print_count = f"T{t_num}C{c_num}"
|
||||||
|
success_records.append({
|
||||||
|
'filename_url': record.filename_url,
|
||||||
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
record.status = 'fail'
|
record.status = 'fail'
|
||||||
_logger.error(f"文件{record.filename_url}打印失败: {str(e)}")
|
_logger.error(f"文件{record.filename_url}打印失败: {str(e)}")
|
||||||
|
failed_records.append({
|
||||||
|
'filename_url': record.filename_url,
|
||||||
|
})
|
||||||
|
if failed_records:
|
||||||
|
message = f"成功打印 {len(success_records)} 个文件,失败 {len(failed_records)} 个"
|
||||||
|
else:
|
||||||
|
message = f"所有 {len(success_records)} 个文件打印成功"
|
||||||
|
return {'message': message}
|
||||||
|
|
||||||
|
|
||||||
class MrpWorkorder(models.Model):
|
class MrpWorkorder(models.Model):
|
||||||
|
|||||||
@@ -4,14 +4,31 @@
|
|||||||
<field name="name">sf.demand.plan.print.wizard.tree</field>
|
<field name="name">sf.demand.plan.print.wizard.tree</field>
|
||||||
<field name="model">sf.demand.plan.print.wizard</field>
|
<field name="model">sf.demand.plan.print.wizard</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree string="打印">
|
<tree string="打印" class="print_demand" js_class="print_demand" >
|
||||||
<field name="model_id"/>
|
<field name="model_id"/>
|
||||||
<field name="filename_url"/>
|
<field name="filename_url"/>
|
||||||
<field name="type"/>
|
<field name="type"/>
|
||||||
<field name="machining_drawings" widget="adaptive_viewer"/>
|
<field name="machining_drawings" attrs="{'column_invisible': True }"/>
|
||||||
<field name="cnc_worksheet" widget="pdf_viewer"/>
|
<field name="cnc_worksheet" attrs="{'column_invisible': True }" />
|
||||||
<field name="status"/>
|
<field name="status"/>
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="action_plan_print_search" model="ir.ui.view">
|
||||||
|
<field name="name">sf.demand.plan.print.wizard.search</field>
|
||||||
|
<field name="model">sf.demand.plan.print.wizard</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search>
|
||||||
|
<field name="type"/>
|
||||||
|
<filter string="图纸" name="filter_type_1" domain="[('type', '=', '1')]"/>
|
||||||
|
<filter string="程序单" name="filter_type_2" domain="[('type', '=', '2')]"/>
|
||||||
|
<filter string="图纸、程序单" name="filter_type_all" domain="[('type', 'in', ('1','2'))]"/>
|
||||||
|
<separator/>
|
||||||
|
<group expand="0" string="Group By">
|
||||||
|
<filter name="group_by_type" string="类型" domain="[]" context="{'group_by': 'type'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
</odoo>
|
</odoo>
|
||||||
@@ -10,6 +10,7 @@ class ResProductTemplate(models.Model):
|
|||||||
model_name = fields.Char('模型名称')
|
model_name = fields.Char('模型名称')
|
||||||
categ_type = fields.Selection(
|
categ_type = fields.Selection(
|
||||||
[("成品", "成品"), ("胚料", "胚料"), ("原材料", "原材料")], string='产品的类别', related='categ_id.type', store=True)
|
[("成品", "成品"), ("胚料", "胚料"), ("原材料", "原材料")], string='产品的类别', related='categ_id.type', store=True)
|
||||||
|
blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类')
|
||||||
model_long = fields.Float('模型长[mm]', digits=(16, 3))
|
model_long = fields.Float('模型长[mm]', digits=(16, 3))
|
||||||
model_width = fields.Float('模型宽[mm]', digits=(16, 3))
|
model_width = fields.Float('模型宽[mm]', digits=(16, 3))
|
||||||
model_height = fields.Float('模型高[mm]', digits=(16, 3))
|
model_height = fields.Float('模型高[mm]', digits=(16, 3))
|
||||||
@@ -74,6 +75,7 @@ class ResProductTemplate(models.Model):
|
|||||||
attachment = self.attachment_create(item['model_name'], item['model_data'])
|
attachment = self.attachment_create(item['model_name'], item['model_data'])
|
||||||
vals = {
|
vals = {
|
||||||
'name': '%s-%s-%s' % ('P', order_id.name, i),
|
'name': '%s-%s-%s' % ('P', order_id.name, i),
|
||||||
|
'blank_type': item.get('blank_type'),
|
||||||
'model_long': item['model_long'] + model_type.embryo_tolerance,
|
'model_long': item['model_long'] + model_type.embryo_tolerance,
|
||||||
'model_width': item['model_width'] + model_type.embryo_tolerance,
|
'model_width': item['model_width'] + model_type.embryo_tolerance,
|
||||||
'model_height': item['model_height'] + model_type.embryo_tolerance,
|
'model_height': item['model_height'] + model_type.embryo_tolerance,
|
||||||
|
|||||||
@@ -95,7 +95,7 @@
|
|||||||
<page string="加工参数">
|
<page string="加工参数">
|
||||||
<group>
|
<group>
|
||||||
<group string="模型">
|
<group string="模型">
|
||||||
<label for="model_long" string="尺寸[mm]"/>
|
<label for="model_long" string="坯料尺寸[mm]"/>
|
||||||
<div class="o_address_format">
|
<div class="o_address_format">
|
||||||
<label for="model_long" string="长"/>
|
<label for="model_long" string="长"/>
|
||||||
<field name="model_long" class="o_address_zip"/>
|
<field name="model_long" class="o_address_zip"/>
|
||||||
@@ -104,6 +104,7 @@
|
|||||||
<label for="model_height" string="高"/>
|
<label for="model_height" string="高"/>
|
||||||
<field name="model_height" class="o_address_zip"/>
|
<field name="model_height" class="o_address_zip"/>
|
||||||
</div>
|
</div>
|
||||||
|
<field name="blank_type" readonly="1"/>
|
||||||
<field name="model_volume" string="体积[mm³]"/>
|
<field name="model_volume" string="体积[mm³]"/>
|
||||||
<field name="product_model_type_id" string="模型类型"/>
|
<field name="product_model_type_id" string="模型类型"/>
|
||||||
<field name="model_processing_panel" placeholder="例如R,U" string="加工面板"
|
<field name="model_processing_panel" placeholder="例如R,U" string="加工面板"
|
||||||
|
|||||||
@@ -377,11 +377,7 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')])
|
line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')])
|
||||||
line_list = list(map(lambda x: x.name, line_list_obj))
|
line_list = list(map(lambda x: x.name, line_list_obj))
|
||||||
# print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
res['LineList'] = ['业绩总览']
|
res['LineList'] = line_list
|
||||||
res['LineList'] += line_list
|
|
||||||
res['LineList'].append('人工线下加工中心')
|
|
||||||
# 增加“业绩总览”与“人工线下加工中心”
|
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
res = {'Succeed': False, 'ErrorCode': 202, 'Error': e}
|
||||||
@@ -405,55 +401,37 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
plan_obj = request.env['sf.production.plan'].sudo()
|
plan_obj = request.env['sf.production.plan'].sudo()
|
||||||
# production_obj = request.env['mrp.production'].sudo()
|
production_obj = request.env['mrp.production'].sudo()
|
||||||
work_order_obj = request.env['mrp.workorder'].sudo()
|
work_order_obj = request.env['mrp.workorder'].sudo()
|
||||||
line_list = ast.literal_eval(kw['line_list'])
|
line_list = ast.literal_eval(kw['line_list'])
|
||||||
|
|
||||||
line_list_obj = request.env['sf.production.line'].sudo().search([('name', 'ilike', 'CNC')])
|
|
||||||
cnc_line_list = list(map(lambda x: x.name, line_list_obj))
|
|
||||||
# print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
for line in line_list:
|
for line in line_list:
|
||||||
|
|
||||||
if line == '业绩总览':
|
|
||||||
work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])]
|
|
||||||
plan_domain = []
|
|
||||||
elif line == '人工线下加工中心':
|
|
||||||
work_order_domain = [('routing_type', '=', '人工线下加工')]
|
|
||||||
plan_domain = [('production_type', '=', '人工线下加工')]
|
|
||||||
else:
|
|
||||||
work_order_domain = [
|
|
||||||
('production_line_id.name', '=', line),
|
|
||||||
('routing_type', '=', 'CNC加工')
|
|
||||||
]
|
|
||||||
plan_domain = [('production_line_id.name', '=', line)]
|
|
||||||
# # 工单计划量
|
# # 工单计划量
|
||||||
# plan_data_total_counts = production_obj.search_count(
|
# plan_data_total_counts = production_obj.search_count(
|
||||||
# [('production_line_id.name', '=', line), ('state', 'not in', ['cancel']),
|
# [('production_line_id.name', '=', line), ('state', 'not in', ['cancel']),
|
||||||
# ('active', '=', True)])
|
# ('active', '=', True)])
|
||||||
|
|
||||||
# 工单计划量切换为CNC工单
|
# 工单计划量切换为CNC工单
|
||||||
plan_data_total = work_order_obj.search(work_order_domain + [
|
plan_data_total_counts = work_order_obj.search_count(
|
||||||
('id', '!=', 8061),
|
[('production_line_id.name', '=', line), ('id', '!=', 8061),
|
||||||
('state', 'in', ['ready', 'progress', 'done'])
|
('state', 'in', ['ready', 'progress', 'done']), ('routing_type', '=', 'CNC加工')])
|
||||||
])
|
|
||||||
|
|
||||||
plan_data_total_counts = sum(plan_data_total.mapped('qty_production'))
|
|
||||||
|
|
||||||
# # 工单完成量
|
# # 工单完成量
|
||||||
# plan_data_finish_counts = plan_obj.search_count(
|
# plan_data_finish_counts = plan_obj.search_count(
|
||||||
# [('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
# [('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
||||||
|
|
||||||
# 工单完成量切换为CNC工单
|
# 工单完成量切换为CNC工单
|
||||||
plan_data_finish = work_order_obj.search(work_order_domain + [
|
plan_data_finish_counts = work_order_obj.search_count(
|
||||||
('state', 'in', ['done'])
|
[('production_line_id.name', '=', line),
|
||||||
])
|
('state', 'in', ['done']), ('routing_type', '=', 'CNC加工')])
|
||||||
|
|
||||||
plan_data_finish_counts = sum(plan_data_finish.mapped('qty_produced'))
|
|
||||||
|
|
||||||
# 超期完成量
|
# 超期完成量
|
||||||
# 搜索所有已经完成的工单
|
# 搜索所有已经完成的工单
|
||||||
plan_data_overtime = work_order_obj.search(work_order_domain + [
|
plan_data_overtime = work_order_obj.search([
|
||||||
('state', 'in', ['done'])
|
('production_line_id.name', '=', line),
|
||||||
|
('state', 'in', ['done']),
|
||||||
|
('routing_type', '=', 'CNC加工')
|
||||||
])
|
])
|
||||||
|
|
||||||
# 使用 filtered 进行字段比较
|
# 使用 filtered 进行字段比较
|
||||||
@@ -462,38 +440,36 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# 获取数量
|
# 获取数量
|
||||||
# plan_data_overtime_counts = len(plan_data_overtime_counts)
|
plan_data_overtime_counts = len(plan_data_overtime_counts)
|
||||||
plan_data_overtime_counts = sum(plan_data_overtime_counts.mapped('qty_produced'))
|
|
||||||
|
|
||||||
# 查找符合条件的生产计划记录
|
# 查找符合条件的生产计划记录
|
||||||
# plan_data = plan_obj.search(plan_domain)
|
plan_data = plan_obj.search([
|
||||||
|
('production_line_id.name', '=', line),
|
||||||
|
])
|
||||||
|
|
||||||
# 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录
|
# 过滤出那些检测结果状态为 '返工' 或 '报废' 的记录
|
||||||
# faulty_plans = plan_data.filtered(lambda p: any(
|
# faulty_plans = plan_data.filtered(lambda p: any(
|
||||||
# result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
|
# result.test_results in ['返工', '报废'] for result in p.production_id.detection_result_ids
|
||||||
# ))
|
# ))
|
||||||
|
|
||||||
faulty_plans = work_order_obj.search(work_order_domain + [
|
faulty_plans = request.env['quality.check'].sudo().search([
|
||||||
('state', 'in', ['scrap', 'rework'])
|
('operation_id.name', '=', 'CNC加工'),
|
||||||
|
('quality_state', 'in', ['fail'])
|
||||||
])
|
])
|
||||||
|
|
||||||
# 查找制造订单取消与归档的数量
|
# 查找制造订单取消与归档的数量
|
||||||
# cancel_order_count = production_obj.search_count(
|
cancel_order_count = production_obj.search_count(
|
||||||
# [('production_line_id.name', '=', line), ('state', 'in', ['cancel']),
|
[('production_line_id.name', '=', line), ('state', 'in', ['cancel']),
|
||||||
# ('active', '=', False)])
|
('active', '=', False)])
|
||||||
|
|
||||||
# 计算符合条件的记录数量
|
# 计算符合条件的记录数量
|
||||||
# plan_data_fault_counts = len(faulty_plans) + cancel_order_count
|
# plan_data_fault_counts = len(faulty_plans) + cancel_order_count
|
||||||
# plan_data_fault_counts = len(faulty_plans)
|
plan_data_fault_counts = len(faulty_plans)
|
||||||
plan_data_fault_counts = sum(faulty_plans.mapped('qty_produced'))
|
|
||||||
|
|
||||||
# 工单返工数量
|
# 工单返工数量
|
||||||
|
|
||||||
plan_data_rework = work_order_obj.search(work_order_domain + [
|
plan_data_rework_counts = plan_obj.search_count(
|
||||||
('state', 'in', ['rework'])
|
[('production_line_id.name', '=', line), ('production_id.state', 'in', ['rework'])])
|
||||||
])
|
|
||||||
|
|
||||||
plan_data_rework_counts = sum(plan_data_rework.mapped('qty_produced'))
|
|
||||||
|
|
||||||
# 工单完成率
|
# 工单完成率
|
||||||
finishe_rate = round(
|
finishe_rate = round(
|
||||||
@@ -503,9 +479,8 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
plan_data_progress_deviation = plan_data_total_counts - plan_data_finish_counts - plan_data_fault_counts
|
plan_data_progress_deviation = plan_data_total_counts - plan_data_finish_counts - plan_data_fault_counts
|
||||||
|
|
||||||
# 完成记录
|
# 完成记录
|
||||||
plan_data_finish_orders = plan_obj.search(plan_domain + [
|
plan_data_finish_orders = plan_obj.search(
|
||||||
('state', 'in', ['finished'])
|
[('production_line_id.name', '=', line), ('state', 'in', ['finished'])])
|
||||||
])
|
|
||||||
|
|
||||||
# # 检测量
|
# # 检测量
|
||||||
# detection_nums = 0
|
# detection_nums = 0
|
||||||
@@ -559,25 +534,25 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
delay_rate = round((delay_num / plan_data_finish_counts if plan_data_finish_counts > 0 else 0), 3)
|
delay_rate = round((delay_num / plan_data_finish_counts if plan_data_finish_counts > 0 else 0), 3)
|
||||||
on_time_rate = 1 - delay_rate
|
on_time_rate = 1 - delay_rate
|
||||||
|
|
||||||
# if plan_data:
|
if plan_data:
|
||||||
data = {
|
data = {
|
||||||
'plan_data_total_counts': plan_data_total_counts,
|
'plan_data_total_counts': plan_data_total_counts,
|
||||||
'plan_data_finish_counts': plan_data_finish_counts,
|
'plan_data_finish_counts': plan_data_finish_counts,
|
||||||
'plan_data_plan_counts': plan_data_total_counts,
|
'plan_data_plan_counts': plan_data_total_counts,
|
||||||
'plan_data_fault_counts': plan_data_fault_counts,
|
'plan_data_fault_counts': plan_data_fault_counts,
|
||||||
'nopass_orders_counts': detection_data - len(pass_nums),
|
'nopass_orders_counts': detection_data - len(pass_nums),
|
||||||
'finishe_rate': finishe_rate,
|
'finishe_rate': finishe_rate,
|
||||||
'plan_data_progress_deviation': plan_data_progress_deviation,
|
'plan_data_progress_deviation': plan_data_progress_deviation,
|
||||||
'plan_data_rework_counts': plan_data_rework_counts,
|
'plan_data_rework_counts': plan_data_rework_counts,
|
||||||
'on_time_rate': on_time_rate,
|
'on_time_rate': on_time_rate,
|
||||||
# 'detection_data': detection_data,
|
# 'detection_data': detection_data,
|
||||||
'detection_data': plan_data_finish_counts,
|
'detection_data': plan_data_finish_counts,
|
||||||
'pass_rate': (plan_data_finish_counts - plan_data_fault_counts) / plan_data_finish_counts,
|
'pass_rate': (plan_data_finish_counts - plan_data_fault_counts) / plan_data_finish_counts,
|
||||||
'plan_data_overtime_counts': plan_data_overtime_counts,
|
'plan_data_overtime_counts': plan_data_overtime_counts,
|
||||||
'overtime_rate': plan_data_overtime_counts / plan_data_finish_counts
|
'overtime_rate': plan_data_overtime_counts / plan_data_finish_counts
|
||||||
if plan_data_finish_counts > 0 else 0,
|
if plan_data_finish_counts > 0 else 0,
|
||||||
}
|
}
|
||||||
res['data'][line] = data
|
res['data'][line] = data
|
||||||
|
|
||||||
return json.dumps(res) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode()
|
return json.dumps(res) # 注意使用 json.dumps 而不是直接用 json.JSONEncoder().encode()
|
||||||
|
|
||||||
@@ -601,11 +576,8 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
line_list = ast.literal_eval(kw['line_list'])
|
line_list = ast.literal_eval(kw['line_list'])
|
||||||
begin_time_str = kw['begin_time'].strip('"')
|
begin_time_str = kw['begin_time'].strip('"')
|
||||||
end_time_str = kw['end_time'].strip('"')
|
end_time_str = kw['end_time'].strip('"')
|
||||||
# 将时间减去8小时(UTC+8转UTC)
|
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
begin_time = (datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S') - timedelta(hours=8))
|
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
end_time = (datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S') - timedelta(hours=8))
|
|
||||||
# begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
|
||||||
# end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
|
||||||
# print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
print('kw', kw)
|
print('kw', kw)
|
||||||
time_unit = kw.get('time_unit', 'day').strip('"') # 默认单位为天
|
time_unit = kw.get('time_unit', 'day').strip('"') # 默认单位为天
|
||||||
@@ -637,42 +609,15 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
current_date += timedelta(days=1)
|
current_date += timedelta(days=1)
|
||||||
return date_list
|
return date_list
|
||||||
|
|
||||||
|
for line in line_list:
|
||||||
|
date_field_name = 'date_finished' # 替换为你模型中的实际字段名
|
||||||
|
order_counts = []
|
||||||
|
|
||||||
if time_unit == 'hour':
|
if time_unit == 'hour':
|
||||||
|
|
||||||
# 计划量,目前只能从mail.message中筛选出
|
|
||||||
plan_order_messages = request.env['mail.message'].sudo().search([
|
|
||||||
('model', '=', 'mrp.workorder'),
|
|
||||||
('create_date', '>=', begin_time.strftime('%Y-%m-%d %H:%M:%S')),
|
|
||||||
('create_date', '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')),
|
|
||||||
('tracking_value_ids.field_desc', '=', '状态'),
|
|
||||||
('tracking_value_ids.new_value_char', '=', '就绪')
|
|
||||||
])
|
|
||||||
|
|
||||||
for line in line_list:
|
|
||||||
date_field_name = 'date_finished' # 替换为你模型中的实际字段名
|
|
||||||
order_counts = []
|
|
||||||
|
|
||||||
if line == '业绩总览':
|
|
||||||
work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])]
|
|
||||||
elif line == '人工线下加工中心':
|
|
||||||
work_order_domain = [('routing_type', '=', '人工线下加工')]
|
|
||||||
else:
|
|
||||||
work_order_domain = [
|
|
||||||
('production_line_id.name', '=', line),
|
|
||||||
('routing_type', '=', 'CNC加工')
|
|
||||||
]
|
|
||||||
time_intervals = get_time_intervals(begin_time, end_time, time_unit)
|
time_intervals = get_time_intervals(begin_time, end_time, time_unit)
|
||||||
print('============================= %s' % time_intervals)
|
print('============================= %s' % time_intervals)
|
||||||
|
|
||||||
time_count_dict = {}
|
time_count_dict = {}
|
||||||
plan_count_dict = {}
|
|
||||||
|
|
||||||
orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [
|
|
||||||
('state', 'in', ['done']),
|
|
||||||
(date_field_name, '>=', begin_time.strftime('%Y-%m-%d %H:%M:%S')),
|
|
||||||
(date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S'))
|
|
||||||
])
|
|
||||||
|
|
||||||
for time_interval in time_intervals:
|
for time_interval in time_intervals:
|
||||||
start_time, end_time = time_interval
|
start_time, end_time = time_interval
|
||||||
@@ -684,106 +629,66 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
# (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
|
# (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
|
||||||
# ])
|
# ])
|
||||||
|
|
||||||
interval_orders = orders.filtered(
|
orders = request.env['mrp.workorder'].sudo().search([
|
||||||
lambda o: o[date_field_name] >= start_time
|
('routing_type', '=', 'CNC加工'), # 将第一个条件合并进来
|
||||||
and o[date_field_name] <= end_time
|
('production_line_id.name', '=', line),
|
||||||
)
|
('state', 'in', ['done']),
|
||||||
|
(date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
|
||||||
|
(date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S'))
|
||||||
|
])
|
||||||
|
|
||||||
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
|
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
|
||||||
key = (start_time + timedelta(hours=8)).strftime('%H:%M:%S') # 只取小时:分钟:秒作为键
|
key = start_time.strftime('%H:%M:%S') # 只取小时:分钟:秒作为键
|
||||||
# time_count_dict[key] = len(orders)
|
time_count_dict[key] = len(orders)
|
||||||
time_count_dict[key] = sum(interval_orders.mapped('qty_produced'))
|
|
||||||
|
|
||||||
for time_interval in time_intervals:
|
|
||||||
start_time, end_time = time_interval
|
|
||||||
|
|
||||||
# orders = plan_obj.search([
|
|
||||||
# ('production_line_id.name', '=', line),
|
|
||||||
# ('state', 'in', ['done']),
|
|
||||||
# (date_field_name, '>=', start_time.strftime('%Y-%m-%d %H:%M:%S')),
|
|
||||||
# (date_field_name, '<=', end_time.strftime('%Y-%m-%d %H:%M:%S')) # 包括结束时间
|
|
||||||
# ])
|
|
||||||
|
|
||||||
interval_plan_orders = plan_order_messages.filtered(
|
|
||||||
lambda o: o.create_date >= start_time
|
|
||||||
and o.create_date <= end_time
|
|
||||||
)
|
|
||||||
|
|
||||||
interval_order_ids = set(interval_plan_orders.mapped('res_id'))
|
|
||||||
|
|
||||||
interval_orders = request.env['mrp.workorder'].sudo().browse(interval_order_ids)
|
|
||||||
if line == '业绩总览':
|
|
||||||
interval_orders = interval_orders.filtered(lambda o: o.routing_type in ['人工线下加工', 'CNC加工'])
|
|
||||||
elif line == '人工线下加工中心':
|
|
||||||
interval_orders = interval_orders.filtered(lambda o: o.routing_type == '人工线下加工')
|
|
||||||
else:
|
|
||||||
interval_orders = interval_orders.filtered(lambda o: o.routing_type == 'CNC加工' and o.production_line_id.name == line)
|
|
||||||
|
|
||||||
# 使用小时和分钟作为键,确保每个小时的数据有独立的键
|
|
||||||
key = (start_time + timedelta(hours=8)).strftime('%H:%M:%S') # 只取小时:分钟:秒作为键
|
|
||||||
# time_count_dict[key] = len(orders)
|
|
||||||
plan_count_dict[key] = sum(interval_orders.mapped('qty_production'))
|
|
||||||
|
|
||||||
# order_counts.append()
|
# order_counts.append()
|
||||||
res['data'][line] = {
|
res['data'][line] = {
|
||||||
'finish_order_nums': time_count_dict,
|
'finish_order_nums': time_count_dict,
|
||||||
'plan_order_nums': plan_count_dict
|
'plan_order_nums': 28
|
||||||
}
|
}
|
||||||
else:
|
return json.dumps(res)
|
||||||
|
|
||||||
for line in line_list:
|
date_list = get_date_list(begin_time, end_time)
|
||||||
date_field_name = 'date_finished' # 替换为你模型中的实际字段名
|
|
||||||
order_counts = []
|
|
||||||
|
|
||||||
if line == '业绩总览':
|
for date in date_list:
|
||||||
work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])]
|
next_day = date + timedelta(days=1)
|
||||||
elif line == '人工线下加工中心':
|
orders = request.env['mrp.workorder'].sudo().search(
|
||||||
work_order_domain = [('routing_type', '=', '人工线下加工')]
|
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['done']),
|
||||||
else:
|
('routing_type', '=', 'CNC加工'),
|
||||||
work_order_domain = [
|
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
|
||||||
('production_line_id.name', '=', line),
|
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
|
||||||
('routing_type', '=', 'CNC加工')
|
])
|
||||||
]
|
|
||||||
|
|
||||||
date_list = get_date_list(begin_time, end_time)
|
rework_orders = request.env['mrp.workorder'].sudo().search(
|
||||||
|
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['rework']),
|
||||||
|
('routing_type', '=', 'CNC加工'),
|
||||||
|
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
|
||||||
|
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
|
||||||
|
])
|
||||||
|
not_passed_orders = request.env['mrp.workorder'].sudo().search(
|
||||||
|
[('production_id.production_line_id.name', '=', line), ('state', 'in', ['scrap', 'cancel']),
|
||||||
|
('routing_type', '=', 'CNC加工'),
|
||||||
|
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
|
||||||
|
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
|
||||||
|
])
|
||||||
|
order_counts.append({
|
||||||
|
'date': date.strftime('%Y-%m-%d'),
|
||||||
|
'order_count': len(orders),
|
||||||
|
'rework_orders': len(rework_orders),
|
||||||
|
'not_passed_orders': len(not_passed_orders)
|
||||||
|
})
|
||||||
|
# 外面包一层,没什么是包一层不能解决的,包一层就能区分了,类似于包一层div
|
||||||
|
# 外面包一层的好处是,可以把多个数据结构打包在一起,方便前端处理
|
||||||
|
|
||||||
for date in date_list:
|
# date_list_dict = {line: order_counts}
|
||||||
next_day = date + timedelta(days=1)
|
|
||||||
orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [
|
|
||||||
('state', 'in', ['done']),
|
|
||||||
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
|
|
||||||
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
|
|
||||||
])
|
|
||||||
|
|
||||||
rework_orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [
|
res['data'][line] = order_counts
|
||||||
('state', 'in', ['rework']),
|
|
||||||
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
|
|
||||||
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
|
|
||||||
])
|
|
||||||
not_passed_orders = request.env['mrp.workorder'].sudo().search(work_order_domain + [
|
|
||||||
('state', 'in', ['scrap', 'cancel']),
|
|
||||||
(date_field_name, '>=', date.strftime('%Y-%m-%d 00:00:00')),
|
|
||||||
(date_field_name, '<', next_day.strftime('%Y-%m-%d 00:00:00'))
|
|
||||||
])
|
|
||||||
order_counts.append({
|
|
||||||
'date': date.strftime('%Y-%m-%d'),
|
|
||||||
'order_count': sum(orders.mapped('qty_produced')),
|
|
||||||
'rework_orders': sum(rework_orders.mapped('qty_produced')),
|
|
||||||
'not_passed_orders': sum(not_passed_orders.mapped('qty_produced'))
|
|
||||||
})
|
|
||||||
# 外面包一层,没什么是包一层不能解决的,包一层就能区分了,类似于包一层div
|
|
||||||
# 外面包一层的好处是,可以把多个数据结构打包在一起,方便前端处理
|
|
||||||
|
|
||||||
# date_list_dict = {line: order_counts}
|
|
||||||
|
|
||||||
res['data'][line] = order_counts
|
|
||||||
return json.dumps(res)
|
return json.dumps(res)
|
||||||
|
|
||||||
# 实时产量
|
# 实时产量
|
||||||
@http.route('/api/RealTimeProduct', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
@http.route('/api/RealTimeProduct', type='http', auth='public', methods=['GET', 'POST'], csrf=False, cors="*")
|
||||||
def RealTimeProduct(self, **kw):
|
def RealTimeProduct(self, **kw):
|
||||||
"""
|
"""
|
||||||
获取实时产量(作废)
|
获取实时产量
|
||||||
:param kw:
|
:param kw:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
@@ -806,21 +711,6 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
|
|
||||||
# 当班计划量
|
# 当班计划量
|
||||||
for line in line_list:
|
for line in line_list:
|
||||||
|
|
||||||
if line == '业绩总览':
|
|
||||||
work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])]
|
|
||||||
plan_domain = []
|
|
||||||
elif line == '人工线下加工中心':
|
|
||||||
work_order_domain = [('routing_type', '=', '人工线下加工')]
|
|
||||||
plan_domain = [('production_type', '=', '人工线下加工')]
|
|
||||||
else:
|
|
||||||
work_order_domain = [
|
|
||||||
('production_line_id.name', '=', line),
|
|
||||||
('routing_type', '=', 'CNC加工')
|
|
||||||
]
|
|
||||||
plan_domain = [('production_line_id.name', '=', line)]
|
|
||||||
|
|
||||||
|
|
||||||
plan_order_nums = plan_obj.search_count(
|
plan_order_nums = plan_obj.search_count(
|
||||||
[('production_line_id.name', '=', line), ('state', 'not in', ['draft']),
|
[('production_line_id.name', '=', line), ('state', 'not in', ['draft']),
|
||||||
('date_planned_start', '>=', begin_time),
|
('date_planned_start', '>=', begin_time),
|
||||||
@@ -862,72 +752,42 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
:param kw:
|
:param kw:
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# res = {'status': 1, 'message': '成功', 'not_done_data': [], 'done_data': []}
|
# res = {'status': 1, 'message': '成功', 'not_done_data': [], 'done_data': []}
|
||||||
res = {'status': 1, 'message': '成功', 'data': {}}
|
res = {'status': 1, 'message': '成功', 'data': {}}
|
||||||
# 解决产品名称取到英文的问题
|
|
||||||
request.update_context(lang='zh_CN')
|
|
||||||
plan_obj = request.env['sf.production.plan'].sudo()
|
plan_obj = request.env['sf.production.plan'].sudo()
|
||||||
work_order_obj = request.env['mrp.workorder'].sudo()
|
|
||||||
# 获取mrp.workorder的state字段的selection内容
|
|
||||||
state_dict = dict(request.env['mrp.workorder'].sudo()._fields['state'].selection)
|
|
||||||
line_list = ast.literal_eval(kw['line_list'])
|
line_list = ast.literal_eval(kw['line_list'])
|
||||||
begin_time_str = kw['begin_time'].strip('"')
|
begin_time_str = kw['begin_time'].strip('"')
|
||||||
end_time_str = kw['end_time'].strip('"')
|
end_time_str = kw['end_time'].strip('"')
|
||||||
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
begin_time = datetime.strptime(begin_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
end_time = datetime.strptime(end_time_str, '%Y-%m-%d %H:%M:%S')
|
||||||
# print('line_list: %s' % line_list)
|
# print('line_list: %s' % line_list)
|
||||||
|
not_done_data = []
|
||||||
|
done_data = []
|
||||||
final_data = {}
|
final_data = {}
|
||||||
|
|
||||||
# 获取当前时间,并计算24小时前的时间
|
|
||||||
current_time = datetime.now()
|
|
||||||
time_48_hours_ago = current_time - timedelta(hours=48)
|
|
||||||
|
|
||||||
# # 计划量,目前只能从mail.message中筛选出
|
|
||||||
# plan_order_messages = request.env['mail.message'].sudo().search([
|
|
||||||
# ('model', '=', 'mrp.workorder'),
|
|
||||||
# ('create_date', '>=', time_48_hours_ago.strftime('%Y-%m-%d %H:%M:%S')),
|
|
||||||
# ('tracking_value_ids.field_desc', '=', '状态'),
|
|
||||||
# ('tracking_value_ids.new_value_char', 'in', ['就绪', '生产中'])
|
|
||||||
# ])
|
|
||||||
|
|
||||||
for line in line_list:
|
for line in line_list:
|
||||||
not_done_data = []
|
|
||||||
done_data = []
|
|
||||||
not_done_index = 1
|
|
||||||
done_index = 1
|
|
||||||
|
|
||||||
if line == '业绩总览':
|
|
||||||
work_order_domain = [('routing_type', 'in', ['人工线下加工', 'CNC加工'])]
|
|
||||||
elif line == '人工线下加工中心':
|
|
||||||
work_order_domain = [('routing_type', '=', '人工线下加工')]
|
|
||||||
else:
|
|
||||||
work_order_domain = [
|
|
||||||
('production_line_id.name', '=', line),
|
|
||||||
('routing_type', '=', 'CNC加工')
|
|
||||||
]
|
|
||||||
# 未完成订单
|
# 未完成订单
|
||||||
# not_done_orders = plan_obj.search(
|
# not_done_orders = plan_obj.search(
|
||||||
# [('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
|
# [('production_line_id.name', '=', line), ('state', 'not in', ['finished']),
|
||||||
# ('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
|
# ('production_id.state', 'not in', ['cancel', 'done']), ('active', '=', True)
|
||||||
# ])
|
# ])
|
||||||
not_done_orders = work_order_obj.search(work_order_domain + [
|
not_done_orders = request.env['mrp.workorder'].sudo().search(
|
||||||
('state', 'in', ['ready', 'progress']),
|
[('production_line_id.name', '=', line), ('state', 'in', ['ready', 'progress']),
|
||||||
('date_planned_start', '>=', time_48_hours_ago),
|
('routing_type', '=', 'CNC加工')
|
||||||
('date_planned_start', '<=', current_time)
|
])
|
||||||
], order='id asc'
|
|
||||||
)
|
|
||||||
|
|
||||||
# 完成订单
|
# 完成订单
|
||||||
# 获取当前时间,并计算24小时前的时间
|
# 获取当前时间,并计算24小时前的时间
|
||||||
# current_time = datetime.now()
|
current_time = datetime.now()
|
||||||
# time_24_hours_ago = current_time - timedelta(hours=24)
|
time_24_hours_ago = current_time - timedelta(hours=24)
|
||||||
|
|
||||||
finish_orders = work_order_obj.search(work_order_domain + [
|
finish_orders = plan_obj.search([
|
||||||
('state', 'in', ['done']),
|
('production_line_id.name', '=', line), ('state', 'in', ['finished']),
|
||||||
('production_id.state', 'not in', ['cancel']),
|
('production_id.state', 'not in', ['cancel']), ('active', '=', True),
|
||||||
('date_finished', '>=', time_48_hours_ago)
|
('actual_end_time', '>=', time_24_hours_ago)
|
||||||
], order='id asc')
|
])
|
||||||
# logging.info('完成订单: %s' % finish_orders)
|
# print(finish_orders)
|
||||||
|
|
||||||
# 获取所有未完成订单的ID列表
|
# 获取所有未完成订单的ID列表
|
||||||
order_ids = [order.id for order in not_done_orders]
|
order_ids = [order.id for order in not_done_orders]
|
||||||
@@ -935,14 +795,14 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
finish_order_ids = [order.id for order in finish_orders]
|
finish_order_ids = [order.id for order in finish_orders]
|
||||||
|
|
||||||
# 对ID进行排序
|
# 对ID进行排序
|
||||||
# sorted_order_ids = sorted(order_ids)
|
sorted_order_ids = sorted(order_ids)
|
||||||
|
|
||||||
# finish_sorted_order_ids = sorted(finish_order_ids)
|
finish_sorted_order_ids = sorted(finish_order_ids)
|
||||||
|
|
||||||
# 创建ID与序号的对应关系
|
# 创建ID与序号的对应关系
|
||||||
# id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(sorted_order_ids)}
|
id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(sorted_order_ids)}
|
||||||
|
|
||||||
# finish_id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(finish_sorted_order_ids)}
|
finish_id_to_sequence = {order_id: index + 1 for index, order_id in enumerate(finish_sorted_order_ids)}
|
||||||
|
|
||||||
# # 输出结果或进一步处理
|
# # 输出结果或进一步处理
|
||||||
# for order_id, sequence in id_to_sequence.items():
|
# for order_id, sequence in id_to_sequence.items():
|
||||||
@@ -963,21 +823,30 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
material_match = re.search(material_pattern, blank_name)
|
material_match = re.search(material_pattern, blank_name)
|
||||||
material = material_match.group(1) if material_match else 'No match found'
|
material = material_match.group(1) if material_match else 'No match found'
|
||||||
|
|
||||||
|
state_dict = {
|
||||||
|
'draft': '待排程',
|
||||||
|
'done': '已排程',
|
||||||
|
'processing': '生产中',
|
||||||
|
'finished': '已完成',
|
||||||
|
'ready': '待加工',
|
||||||
|
'progress': '生产中',
|
||||||
|
}
|
||||||
|
|
||||||
line_dict = {
|
line_dict = {
|
||||||
'sequence': not_done_index,
|
'sequence': id_to_sequence[order.id],
|
||||||
'workorder_name': order.production_id.name,
|
'workorder_name': order.production_id.name,
|
||||||
'blank_name': blank_name,
|
'blank_name': blank_name,
|
||||||
'material': material,
|
'material': material,
|
||||||
'dimensions': dimensions,
|
'dimensions': dimensions,
|
||||||
'order_qty': order.qty_production,
|
'order_qty': 1,
|
||||||
'state': state_dict[order.state],
|
'state': state_dict[order.state],
|
||||||
|
|
||||||
}
|
}
|
||||||
not_done_data.append(line_dict)
|
not_done_data.append(line_dict)
|
||||||
not_done_index += 1
|
|
||||||
|
|
||||||
for finish_order in finish_orders:
|
for finish_order in finish_orders:
|
||||||
|
if not finish_order.actual_end_time:
|
||||||
|
continue
|
||||||
blank_name = ''
|
blank_name = ''
|
||||||
try:
|
try:
|
||||||
blank_name = finish_order.production_id.move_raw_ids[0].product_id.name
|
blank_name = finish_order.production_id.move_raw_ids[0].product_id.name
|
||||||
@@ -992,18 +861,17 @@ class Sf_Dashboard_Connect(http.Controller):
|
|||||||
material = material_match.group(1) if material_match else 'No match found'
|
material = material_match.group(1) if material_match else 'No match found'
|
||||||
|
|
||||||
line_dict = {
|
line_dict = {
|
||||||
'sequence': done_index,
|
'sequence': finish_id_to_sequence[finish_order.id],
|
||||||
'workorder_name': finish_order.production_id.name,
|
'workorder_name': finish_order.name,
|
||||||
'blank_name': blank_name,
|
'blank_name': blank_name,
|
||||||
'material': material,
|
'material': material,
|
||||||
'dimensions': dimensions,
|
'dimensions': dimensions,
|
||||||
'order_qty': finish_order.qty_produced,
|
'order_qty': finish_order.product_qty,
|
||||||
'finish_time': finish_order.date_finished.strftime(
|
'finish_time': finish_order.actual_end_time.strftime(
|
||||||
'%Y-%m-%d %H:%M:%S') if finish_order.date_finished else ' '
|
'%Y-%m-%d %H:%M:%S') if finish_order.actual_end_time else ' '
|
||||||
|
|
||||||
}
|
}
|
||||||
done_data.append(line_dict)
|
done_data.append(line_dict)
|
||||||
done_index += 1
|
|
||||||
|
|
||||||
# 开始包一层
|
# 开始包一层
|
||||||
res['data'][line] = {'not_done_data': not_done_data, 'done_data': done_data}
|
res['data'][line] = {'not_done_data': not_done_data, 'done_data': done_data}
|
||||||
|
|||||||
@@ -45,9 +45,8 @@ class JikimoSaleRoutePicking(Sf_Bf_Connect):
|
|||||||
product.product_tmpl_id.is_customer_provided = True if item['embryo_redundancy_id'] else False
|
product.product_tmpl_id.is_customer_provided = True if item['embryo_redundancy_id'] else False
|
||||||
order_id.with_user(request.env.ref("base.user_admin")).sale_order_create_line(product, item)
|
order_id.with_user(request.env.ref("base.user_admin")).sale_order_create_line(product, item)
|
||||||
i += 1
|
i += 1
|
||||||
if kw.get('contract_file_name') and kw.get('contract_file') and kw.get('contract_code'):
|
# BFM 内部下单 新增合同等内容补充
|
||||||
order_id.create_sale_documents(kw.get('contract_file_name'), kw.get('contract_file'))
|
order_id.write_sale_documents(kw)
|
||||||
order_id.write({'contract_code': kw.get('contract_code'), 'contract_date': kw.get('contract_date')})
|
|
||||||
res['factory_order_no'] = order_id.name
|
res['factory_order_no'] = order_id.name
|
||||||
order_id.confirm_to_supply_method()
|
order_id.confirm_to_supply_method()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ class ResProductMo(models.Model):
|
|||||||
model_file = fields.Binary('模型文件')
|
model_file = fields.Binary('模型文件')
|
||||||
categ_type = fields.Selection(string='产品的类别', related='categ_id.type', store=True)
|
categ_type = fields.Selection(string='产品的类别', related='categ_id.type', store=True)
|
||||||
model_name = fields.Char('模型名称')
|
model_name = fields.Char('模型名称')
|
||||||
|
blank_type = fields.Selection([('圆料', '圆料'), ('方料', '方料')], string='坯料分类')
|
||||||
model_long = fields.Float('模型长(mm)', digits=(16, 3))
|
model_long = fields.Float('模型长(mm)', digits=(16, 3))
|
||||||
model_width = fields.Float('模型宽(mm)', digits=(16, 3))
|
model_width = fields.Float('模型宽(mm)', digits=(16, 3))
|
||||||
model_height = fields.Float('模型高(mm)', digits=(16, 3))
|
model_height = fields.Float('模型高(mm)', digits=(16, 3))
|
||||||
@@ -895,6 +896,7 @@ class ResProductMo(models.Model):
|
|||||||
product_name = self.generate_product_name(order_id, item, i)
|
product_name = self.generate_product_name(order_id, item, i)
|
||||||
vals = {
|
vals = {
|
||||||
'name': product_name,
|
'name': product_name,
|
||||||
|
'blank_type': item.get('blank_type'),
|
||||||
'model_long': self.format_float(item['model_long'] + embryo_redundancy_id.long),
|
'model_long': self.format_float(item['model_long'] + embryo_redundancy_id.long),
|
||||||
'model_width': self.format_float(item['model_width'] + embryo_redundancy_id.width),
|
'model_width': self.format_float(item['model_width'] + embryo_redundancy_id.width),
|
||||||
'model_height': self.format_float(item['model_height'] + embryo_redundancy_id.height),
|
'model_height': self.format_float(item['model_height'] + embryo_redundancy_id.height),
|
||||||
|
|||||||
@@ -193,6 +193,19 @@ class SaleOrder(models.Model):
|
|||||||
'target': 'new',
|
'target': 'new',
|
||||||
'res_id': wizard.id,
|
'res_id': wizard.id,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def write_sale_documents(self, kw):
|
||||||
|
"""BFM 内部下单 内容补充 """
|
||||||
|
val = {}
|
||||||
|
if kw.get('contract_file_name') and kw.get('contract_file'):
|
||||||
|
document_id = self.create_sale_documents(kw.get('contract_file_name'), kw.get('contract_file'))
|
||||||
|
val.update({'contract_document_id': document_id.id})
|
||||||
|
if kw.get('contract_code') or kw.get('contract_date'):
|
||||||
|
val.update({'contract_code': kw.get('contract_code'), 'contract_date': kw.get('contract_date')})
|
||||||
|
if kw.get('customer_name'):
|
||||||
|
val.update({'customer_name': kw.get('customer_name')})
|
||||||
|
self.write(val)
|
||||||
|
|
||||||
def create_sale_documents(self, contract_file_name, contract_file):
|
def create_sale_documents(self, contract_file_name, contract_file):
|
||||||
# 创建ir.attachment记录
|
# 创建ir.attachment记录
|
||||||
attachment = self.env['ir.attachment'].sudo().create({
|
attachment = self.env['ir.attachment'].sudo().create({
|
||||||
@@ -214,9 +227,7 @@ class SaleOrder(models.Model):
|
|||||||
'res_id': self.id,
|
'res_id': self.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
self.write({
|
return document
|
||||||
'contract_document_id': document.id
|
|
||||||
})
|
|
||||||
|
|
||||||
class SaleOrderLine(models.Model):
|
class SaleOrderLine(models.Model):
|
||||||
_inherit = 'sale.order.line'
|
_inherit = 'sale.order.line'
|
||||||
|
|||||||
@@ -66,7 +66,7 @@
|
|||||||
<attribute name="string">不合格</attribute>
|
<attribute name="string">不合格</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//header//button[@name='do_fail'][2]" position="attributes">
|
<xpath expr="//header//button[@name='do_fail'][2]" position="attributes">
|
||||||
<attribute name="attrs">{'invisible': ['|',('quality_state', '!=', 'pass'),('work_state','in', ('done', 'rework'))]}</attribute>
|
<attribute name="attrs">{'invisible': ['|','|',('quality_state', '!=', 'pass'),('work_state','in', ('done', 'rework')),'&',('quality_state', '=', 'pass'), ('test_type', '=', '出厂检验报告')]}</attribute>
|
||||||
<attribute name="string">不合格</attribute>
|
<attribute name="string">不合格</attribute>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ class ReSaleOrder(models.Model):
|
|||||||
|
|
||||||
model_display_version = fields.Char('模型展示版本', default="v1")
|
model_display_version = fields.Char('模型展示版本', default="v1")
|
||||||
|
|
||||||
|
customer_name = fields.Char('终端客户')
|
||||||
contract_code = fields.Char('合同编号')
|
contract_code = fields.Char('合同编号')
|
||||||
contract_date = fields.Date('合同日期')
|
contract_date = fields.Date('合同日期')
|
||||||
contract_document_id = fields.Many2one('documents.document', string='合同文件')
|
contract_document_id = fields.Many2one('documents.document', string='合同文件')
|
||||||
@@ -158,6 +159,7 @@ class ReSaleOrder(models.Model):
|
|||||||
'manual_quotation': item.get('manual_quotation'),
|
'manual_quotation': item.get('manual_quotation'),
|
||||||
'model_id': item['model_id'],
|
'model_id': item['model_id'],
|
||||||
'delivery_end_date': item['delivery_end_date'],
|
'delivery_end_date': item['delivery_end_date'],
|
||||||
|
'customer_delivery_date': item.get('customer_delivery_date'),
|
||||||
}
|
}
|
||||||
return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals)
|
return self.env['sale.order.line'].with_context(skip_procurement=True).create(vals)
|
||||||
|
|
||||||
@@ -291,8 +293,8 @@ class ResaleOrderLine(models.Model):
|
|||||||
manual_quotation = fields.Boolean('人工编程', default=False)
|
manual_quotation = fields.Boolean('人工编程', default=False)
|
||||||
model_url = fields.Char('模型文件地址')
|
model_url = fields.Char('模型文件地址')
|
||||||
model_id = fields.Char('模型ID')
|
model_id = fields.Char('模型ID')
|
||||||
|
|
||||||
delivery_end_date = fields.Date('交货截止日期')
|
delivery_end_date = fields.Date('交货截止日期')
|
||||||
|
customer_delivery_date = fields.Date('客户交期')
|
||||||
|
|
||||||
@api.depends('embryo_redundancy_id')
|
@api.depends('embryo_redundancy_id')
|
||||||
def _compute_is_incoming_material(self):
|
def _compute_is_incoming_material(self):
|
||||||
|
|||||||
@@ -90,6 +90,9 @@
|
|||||||
<field name="partner_id" position="replace">
|
<field name="partner_id" position="replace">
|
||||||
<field name="partner_id" widget="res_partner_many2one" context="{'is_customer': True }"
|
<field name="partner_id" widget="res_partner_many2one" context="{'is_customer': True }"
|
||||||
options='{"always_reload": True,"no_create": True}'/>
|
options='{"always_reload": True,"no_create": True}'/>
|
||||||
|
<field name="customer_name" readonly="1"/>
|
||||||
|
<field name="contract_code" readonly="1"/>
|
||||||
|
<field name="contract_date" readonly="1"/>
|
||||||
</field>
|
</field>
|
||||||
<field name="payment_term_id" position="attributes">
|
<field name="payment_term_id" position="attributes">
|
||||||
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
<attribute name="attrs">{'readonly': [('state', 'in', ['cancel','sale'])]}</attribute>
|
||||||
@@ -124,6 +127,7 @@
|
|||||||
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="replace">
|
<xpath expr="//field[@name='order_line']/tree/field[@name='name']" position="replace">
|
||||||
<field name="name" widget="section_and_note_text" optional="show"
|
<field name="name" widget="section_and_note_text" optional="show"
|
||||||
string="参数说明(长/宽/高/体积/精度/材质)"/>
|
string="参数说明(长/宽/高/体积/精度/材质)"/>
|
||||||
|
<field name="customer_delivery_date" readonly="1"/>
|
||||||
<field name="manual_quotation" readonly="1"/>
|
<field name="manual_quotation" readonly="1"/>
|
||||||
<field name="is_incoming_material" readonly="1"/>
|
<field name="is_incoming_material" readonly="1"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|||||||
@@ -818,6 +818,7 @@ class FunctionalToolAssembly(models.Model):
|
|||||||
|
|
||||||
def _get_old_tool_material_lot(self, material_ids):
|
def _get_old_tool_material_lot(self, material_ids):
|
||||||
""" 根据先进先出原则选择物料批次 """
|
""" 根据先进先出原则选择物料批次 """
|
||||||
|
material_ids = material_ids.filtered(lambda m: m.tracking != 'none')
|
||||||
location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
|
location_id = self.env['stock.location'].search([('name', '=', '刀具房')])
|
||||||
stock_quant = self.env['stock.quant'].sudo().search(
|
stock_quant = self.env['stock.quant'].sudo().search(
|
||||||
[('location_id', '=', location_id.id), ('product_id', 'in', material_ids.ids), ('quantity', '>', '0')],
|
[('location_id', '=', location_id.id), ('product_id', 'in', material_ids.ids), ('quantity', '>', '0')],
|
||||||
|
|||||||
Reference in New Issue
Block a user