Merge branch 'refs/heads/develop' into feature/tax_sync
This commit is contained in:
61
sf_base/static/src/js/custom_barcode_handlers.js
Normal file
61
sf_base/static/src/js/custom_barcode_handlers.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { barcodeGenericHandlers } from '@barcodes/barcode_handlers';
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
// 定义新的 clickOnButton 函数
|
||||
function customClickOnButton(selector) {
|
||||
console.log("This is the custom clickOnButton function!");
|
||||
|
||||
const buttons = document.body.querySelectorAll(selector);
|
||||
|
||||
let length = buttons.length;
|
||||
if (length > 0) {
|
||||
buttons[length - 1].click();
|
||||
} else {
|
||||
console.warn(`Button with selector ${selector} not found`);
|
||||
}
|
||||
}
|
||||
|
||||
patch(barcodeGenericHandlers, "start", {
|
||||
start(env, { ui, barcode, notification }) {
|
||||
// 使用新定义的 clickOnButton 函数
|
||||
const COMMANDS = {
|
||||
"O-CMD.EDIT": () => customClickOnButton(".o_form_button_edit"),
|
||||
"O-CMD.DISCARD": () => customClickOnButton(".o_form_button_cancel"),
|
||||
"O-CMD.SAVE": () => customClickOnButton(".o_form_button_save"),
|
||||
"O-CMD.PREV": () => customClickOnButton(".o_pager_previous"),
|
||||
"O-CMD.NEXT": () => customClickOnButton(".o_pager_next"),
|
||||
"O-CMD.PAGER-FIRST": () => updatePager("first"),
|
||||
"O-CMD.PAGER-LAST": () => updatePager("last"),
|
||||
"O-CMD.CONFIRM": () => customClickOnButton(".jikimo_button_confirm"),
|
||||
};
|
||||
|
||||
barcode.bus.addEventListener("barcode_scanned", (ev) => {
|
||||
const barcode = ev.detail.barcode;
|
||||
if (barcode.startsWith("O-BTN.")) {
|
||||
let targets = [];
|
||||
try {
|
||||
targets = getVisibleElements(ui.activeElement, `[barcode_trigger=${barcode.slice(6)}]`);
|
||||
} catch (_e) {
|
||||
console.warn(`Barcode '${barcode}' is not valid`);
|
||||
}
|
||||
for (let elem of targets) {
|
||||
elem.click();
|
||||
}
|
||||
}
|
||||
if (barcode.startsWith("O-CMD.")) {
|
||||
const fn = COMMANDS[barcode];
|
||||
if (fn) {
|
||||
fn();
|
||||
} else {
|
||||
notification.add(env._t("Barcode: ") + `'${barcode}'`, {
|
||||
title: env._t("Unknown barcode command"),
|
||||
type: "danger"
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -5,9 +5,12 @@ import { registry } from '@web/core/registry';
|
||||
import { formView } from '@web/views/form/form_view';
|
||||
import { FormController } from '@web/views/form/form_controller';
|
||||
|
||||
import { listView } from '@web/views/list/list_view';
|
||||
import { ListController } from '@web/views/list/list_controller'
|
||||
|
||||
import { onRendered, onMounted } from "@odoo/owl";
|
||||
|
||||
export class RemoveFocusController extends FormController {
|
||||
export class RemoveFocusFormController extends FormController {
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
@@ -17,7 +20,23 @@ export class RemoveFocusController extends FormController {
|
||||
}
|
||||
}
|
||||
|
||||
registry.category('views').add('remove_focus_view', {
|
||||
registry.category('views').add('remove_focus_form_view', {
|
||||
...formView,
|
||||
Controller: RemoveFocusController,
|
||||
Controller: RemoveFocusFormController,
|
||||
});
|
||||
|
||||
|
||||
export class RemoveFocusListController extends ListController {
|
||||
setup() {
|
||||
super.setup();
|
||||
|
||||
onMounted(() => {
|
||||
this.__owl__.bdom.el.querySelectorAll(':focus').forEach(element => element.blur());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
registry.category('views').add('remove_focus_list_view', {
|
||||
...listView,
|
||||
Controller: RemoveFocusListController,
|
||||
});
|
||||
@@ -12,7 +12,6 @@
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="mrs_production_process_parameter_form">
|
||||
<field name="model">sf.production.process.parameter</field>
|
||||
<field name="arch" type="xml">
|
||||
@@ -26,19 +25,19 @@
|
||||
<group>
|
||||
<group>
|
||||
<field name="code" readonly="1"/>
|
||||
<field name="process_id" readonly="1"/>
|
||||
<field name="process_description" readonly="1"/>
|
||||
<field name="process_id" attrs="{'readonly': [('code', '!=', False)]}"/>
|
||||
<field name="process_description" attrs="{'readonly': [('code', '!=', False)]}"/>
|
||||
<field name="gain_way"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="processing_day" readonly="1"/>
|
||||
<field name="travel_day" readonly="1"/>
|
||||
<field name="processing_mm" readonly="1"/>
|
||||
<field name="processing_day" attrs="{'readonly': [('code', '!=', False)]}"/>
|
||||
<field name="travel_day" attrs="{'readonly': [('code', '!=', False)]}"/>
|
||||
<field name="processing_mm" attrs="{'readonly': [('code', '!=', False)]}"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="适用材料">
|
||||
<field name="materials_model_ids" readonly="1"></field>
|
||||
<field name="materials_model_ids" attrs="{'readonly': [('code', '!=', False)]}"></field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
|
||||
@@ -53,7 +53,7 @@ class StatusChange(models.Model):
|
||||
if not ret.get('error'):
|
||||
logging.info('接口已经执行=============')
|
||||
else:
|
||||
logging.error('工厂加工同步订单状态失败 {}'.format(ret.text))
|
||||
logging.error('工厂加工同步订单状态失败 {}'.format(ret))
|
||||
raise UserError('工厂加工同步订单状态失败')
|
||||
except UserError as e:
|
||||
logging.error('工厂加工同步订单状态失败 {}'.format(e))
|
||||
|
||||
@@ -24,6 +24,7 @@ class SfEquipmentSaintenanceStandards(models.Model):
|
||||
remark = fields.Char('备注')
|
||||
maintenance_type = fields.Selection([('保养', '保养'), ("检修", "检修")], string='类型', default='保养')
|
||||
name = fields.Char(string='名称')
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
<field name="name">equipment.maintenance.standards.form</field>
|
||||
<field name="model">equipment.maintenance.standards</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="设备维保标准">
|
||||
<form string="设备维保标准" delete="false" duplicate="false">
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="code" readonly="1" force_save="1"/>
|
||||
<field name="name" readonly="1" force_save="1"/>
|
||||
<field name="maintenance_equipment_category_id" required="1" />
|
||||
<field name="eq_maintenance_ids" invisible='1'/>
|
||||
<field name="overhaul_ids" invisible='1'/>
|
||||
<group>
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="code" readonly="1" force_save="1"/>
|
||||
<field name="name" readonly="1" force_save="1"/>
|
||||
<field name="maintenance_equipment_category_id" required="1"/>
|
||||
<field name="eq_maintenance_ids" invisible='1'/>
|
||||
<field name="overhaul_ids" invisible='1'/>
|
||||
|
||||
|
||||
</group>
|
||||
@@ -50,7 +51,8 @@
|
||||
<field name="name">equipment.maintenance.standards.tree</field>
|
||||
<field name="model">equipment.maintenance.standards</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="设备维保标准">
|
||||
<tree string="设备维保标准" delete="false">
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="code" readonly="1" force_save="1"/>
|
||||
<field name="maintenance_type" required="1"/>
|
||||
<field name="name" required="1"/>
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
'sf_manufacturing/static/src/scss/kanban_change.scss',
|
||||
'sf_manufacturing/static/src/xml/button_show_on_tree.xml',
|
||||
'sf_manufacturing/static/src/js/workpiece_delivery_wizard_confirm.js',
|
||||
'sf_manufacturing/static/src/js/custom_barcode_handlers.js',
|
||||
]
|
||||
|
||||
},
|
||||
|
||||
@@ -477,7 +477,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
logging.info('LocationChange error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||
@http.route('/AutoDeviceApi/AGVToProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def AGVToProduct(self, **kw):
|
||||
"""
|
||||
@@ -549,7 +549,7 @@ class Manufacturing_Connect(http.Controller):
|
||||
logging.info('AGVToProduct error:%s' % e)
|
||||
return json.JSONEncoder().encode(res)
|
||||
|
||||
@http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='sf_token', methods=['GET', 'POST'], csrf=False,
|
||||
@http.route('/AutoDeviceApi/AGVDownProduct', type='json', auth='none', methods=['GET', 'POST'], csrf=False,
|
||||
cors="*")
|
||||
def AGVDownProduct(self, **kw):
|
||||
"""
|
||||
|
||||
@@ -265,3 +265,17 @@ class ResMrpWorkOrder(models.Model):
|
||||
'sf_agv_scheduling_mrp_workorder_ref',
|
||||
string='AGV调度',
|
||||
domain=[('state', '!=', '已取消')])
|
||||
|
||||
def get_down_product_agv_scheduling(self):
|
||||
"""
|
||||
获取关联的制造订单下产线的agv任务
|
||||
"""
|
||||
workorder_ids = self.production_id.workorder_ids
|
||||
cnc_workorder = workorder_ids.filtered(
|
||||
lambda w: w.routing_type == 'CNC加工' and w.state == 'done' and w.processing_panel == self.processing_panel
|
||||
)
|
||||
if cnc_workorder:
|
||||
agv_schedulingss = cnc_workorder.agv_scheduling_ids
|
||||
return agv_schedulingss.filtered(lambda a: a.state == '已配送' and a.agv_route_type == '下产线')
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -779,7 +779,8 @@ class MrpProduction(models.Model):
|
||||
routing_workcenter = self.env['mrp.routing.workcenter'].sudo().search(
|
||||
[('name', '=', work.routing_type)])
|
||||
|
||||
work.write({'date_planned_start': date_planned_start, 'date_planned_finished': date_planned_end,'duration_expected':routing_workcenter.time_cycle})
|
||||
work.write({'date_planned_start': date_planned_start, 'date_planned_finished': date_planned_end,
|
||||
'duration_expected': routing_workcenter.time_cycle})
|
||||
|
||||
# 修改标记已完成方法
|
||||
def button_mark_done1(self):
|
||||
@@ -1078,27 +1079,22 @@ class MrpProduction(models.Model):
|
||||
productions.write({'programming_no': self.programming_no, 'is_remanufacture': True})
|
||||
# productions.procurement_group_id.mrp_production_ids.move_dest_ids.write(
|
||||
# {'group_id': self.env['procurement.group'].search([('name', '=', sale_order.name)])})
|
||||
stock_picking = None
|
||||
pc_picking = self.env['stock.picking'].search(
|
||||
[('origin', '=', productions.name), ('name', 'ilike', 'WH/PC/')])
|
||||
stock_picking = pc_picking
|
||||
int_picking = self.env['stock.picking'].search(
|
||||
[('origin', '=', productions.name), ('name', 'ilike', 'WH/INT/')])
|
||||
stock_picking |= int_picking
|
||||
for pick in stock_picking:
|
||||
if pick.move_ids:
|
||||
product_type_id = pick.move_ids[0].product_id.categ_id
|
||||
if product_type_id.name == '坯料':
|
||||
location_id = self.env['stock.location'].search([('name', '=', '坯料存货区')])
|
||||
if not location_id:
|
||||
logging.info(f'没有搜索到【坯料存货区】: {location_id}')
|
||||
break
|
||||
if pick.picking_type_id.name == '内部调拨':
|
||||
if pick.location_dest_id.product_type != product_type_id:
|
||||
pick.location_dest_id = location_id.id
|
||||
elif pick.picking_type_id.name == '生产发料':
|
||||
if pick.location_id.product_type != product_type_id:
|
||||
pick.location_id = location_id.id
|
||||
stock_picking_remanufacture = self.env['stock.picking'].search([('origin', '=', productions.name)])
|
||||
for pick in stock_picking_remanufacture:
|
||||
if pick.name.startswith('WH/PC/') or pick.name.startswith('WH/INT/'):
|
||||
if pick.move_ids:
|
||||
product_type_id = pick.move_ids[0].product_id.categ_id
|
||||
if product_type_id.name == '坯料':
|
||||
location_id = self.env['stock.location'].search([('name', '=', '坯料存货区')])
|
||||
if not location_id:
|
||||
logging.info(f'没有搜索到【坯料存货区】: {location_id}')
|
||||
break
|
||||
if pick.picking_type_id.name == '内部调拨':
|
||||
if pick.location_dest_id.product_type != product_type_id:
|
||||
pick.location_dest_id = location_id.id
|
||||
elif pick.picking_type_id.name == '生产发料':
|
||||
if pick.location_id.product_type != product_type_id:
|
||||
pick.location_id = location_id.id
|
||||
scarp_process_parameter_workorder = self.env['mrp.workorder'].search(
|
||||
[('surface_technics_parameters_id', '!=', False), ('production_id', '=', self.id),
|
||||
('is_subcontract', '=', True)])
|
||||
@@ -1111,7 +1107,6 @@ class MrpProduction(models.Model):
|
||||
for process_item in scarp_process_parameter_workorder:
|
||||
if purchase_item.product_id.categ_type == '表面工艺':
|
||||
if purchase_item.product_id.server_product_process_parameters_id == process_item.surface_technics_parameters_id:
|
||||
print(purchase_orders.origin.find(productions.name))
|
||||
if purchase_orders.origin.find(productions.name) == -1:
|
||||
purchase_orders.origin += ',' + productions.name
|
||||
if item['is_reprogramming'] is False:
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import datetime
|
||||
from datetime import timedelta, time
|
||||
from collections import defaultdict
|
||||
from odoo import fields, models
|
||||
from odoo import fields, models, api
|
||||
from odoo.addons.resource.models.resource import Intervals
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
class ResWorkcenter(models.Model):
|
||||
@@ -41,14 +43,16 @@ class ResWorkcenter(models.Model):
|
||||
|
||||
oee_target = fields.Float(
|
||||
string='OEE Target', help="Overall Effective Efficiency Target in percentage", default=90, tracking=True)
|
||||
oee = fields.Float(compute='_compute_oee', help='Overall Equipment Effectiveness, based on the last month', store=True)
|
||||
oee = fields.Float(compute='_compute_oee', help='Overall Equipment Effectiveness, based on the last month',
|
||||
store=True)
|
||||
|
||||
time_start = fields.Float('Setup Time', tracking=True)
|
||||
time_stop = fields.Float('Cleanup Time', tracking=True)
|
||||
costs_hour = fields.Float(string='Cost per hour', help='Hourly processing cost.', default=0.0, tracking=True)
|
||||
|
||||
equipment_status = fields.Selection(
|
||||
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"), ("封存(报废)", "封存(报废)")],
|
||||
[("正常", "正常"), ("故障停机", "故障停机"), ("计划维保", "计划维保"), ("空闲", "空闲"),
|
||||
("封存(报废)", "封存(报废)")],
|
||||
string="设备状态", related='equipment_id.state')
|
||||
|
||||
# @api.depends('equipment_id')
|
||||
@@ -127,6 +131,102 @@ class ResWorkcenter(models.Model):
|
||||
|
||||
# AGV是否可配送
|
||||
is_agv_scheduling = fields.Boolean(string="AGV所属区域", tracking=True)
|
||||
# 生产线优化
|
||||
available_machine_number = fields.Integer(string="可用机台数量")
|
||||
single_machine_capacity = fields.Float(string="单台小时产能")
|
||||
production_line_hour_capacity = fields.Float(string="生产线小时产能", readonly=True,
|
||||
compute='_compute_production_line_hour_capacity')
|
||||
effective_working_hours_day = fields.Float(string="日有效工作时长", default=0, readonly=True,
|
||||
compute='_compute_effective_working_hours_day')
|
||||
default_capacity = fields.Float(
|
||||
string='生产线日产能', compute='_compute_production_line_day_capacity', readonly=True)
|
||||
|
||||
# 计算生产线日产能
|
||||
@api.depends('production_line_hour_capacity', 'effective_working_hours_day')
|
||||
def _compute_production_line_day_capacity(self):
|
||||
for record in self:
|
||||
record.default_capacity = round(
|
||||
record.production_line_hour_capacity * record.effective_working_hours_day, 2)
|
||||
|
||||
# 计算日有效工作时长
|
||||
@api.depends('resource_calendar_id', 'resource_calendar_id.attendance_ids',
|
||||
'resource_calendar_id.attendance_ids.hour_to', 'resource_calendar_id.attendance_ids.hour_from')
|
||||
def _compute_effective_working_hours_day(self):
|
||||
for record in self:
|
||||
attendance_ids = [p for p in record.resource_calendar_id.attendance_ids if
|
||||
p.dayofweek == self.get_current_day_of_week(datetime.datetime.now())]
|
||||
if attendance_ids:
|
||||
for attendance_id in attendance_ids:
|
||||
if attendance_id.hour_from and attendance_id.hour_to:
|
||||
record.effective_working_hours_day += attendance_id.hour_to - attendance_id.hour_from
|
||||
else:
|
||||
record.effective_working_hours_day = 0
|
||||
|
||||
# 获取传入时间是星期几
|
||||
def get_current_day_of_week(self, datetime):
|
||||
day_num = datetime.weekday()
|
||||
return str(day_num)
|
||||
|
||||
# 计算生产线小时产能
|
||||
@api.depends('single_machine_capacity', 'available_machine_number')
|
||||
def _compute_production_line_hour_capacity(self):
|
||||
for record in self:
|
||||
record.production_line_hour_capacity = round(
|
||||
record.single_machine_capacity * record.available_machine_number, 2)
|
||||
|
||||
# 判断计划开始时间是否在配置的工作中心的工作日历内
|
||||
def deal_with_workcenter_calendar(self, start_date):
|
||||
start_date = start_date + timedelta(hours=8) # 转换为北京时间
|
||||
for record in self:
|
||||
attendance_ids = [p for p in record.resource_calendar_id.attendance_ids if
|
||||
p.dayofweek == record.get_current_day_of_week(start_date) and self.is_between_times(
|
||||
p.hour_from, p.hour_to, start_date)]
|
||||
return False if not attendance_ids else True
|
||||
|
||||
# 判断传入时间是否在配置的工作中心的工作日历内
|
||||
def is_between_times(self, hour_from, hour_to, start_date):
|
||||
integer_part, decimal_part = self.get_integer_and_decimal_parts(hour_from)
|
||||
start_time = time(integer_part, decimal_part)
|
||||
integer_part, decimal_part = self.get_integer_and_decimal_parts(hour_to)
|
||||
end_time = time(integer_part, decimal_part)
|
||||
return start_time <= start_date.time() <= end_time
|
||||
|
||||
# 获取整数部分和小数部分
|
||||
def get_integer_and_decimal_parts(self, value):
|
||||
integer_part = int(value)
|
||||
decimal_part = value - integer_part
|
||||
return int(integer_part), int(decimal_part)
|
||||
|
||||
# 处理排程是否超过日产能
|
||||
def deal_available_default_capacity(self, date_planned):
|
||||
date_planned_start = date_planned.strftime('%Y-%m-%d')
|
||||
date_planned_end = date_planned + timedelta(days=1)
|
||||
date_planned_end = date_planned_end.strftime('%Y-%m-%d')
|
||||
plan_ids = self.env['sf.production.plan'].sudo().search([('date_planned_start', '>=', date_planned_start),
|
||||
('date_planned_start', '<',
|
||||
date_planned_end), ('state', '!=', 'draft')])
|
||||
if plan_ids:
|
||||
sum_qty = sum([p.product_qty for p in plan_ids])
|
||||
if sum_qty >= self.default_capacity:
|
||||
return False
|
||||
return True
|
||||
|
||||
# 处理排程是否超过小时产能
|
||||
def deal_available_single_machine_capacity(self, date_planned):
|
||||
|
||||
date_planned_start = date_planned.strftime('%Y-%m-%d %H:00:00')
|
||||
date_planned_end = date_planned + timedelta(hours=1)
|
||||
date_planned_end = date_planned_end.strftime('%Y-%m-%d %H:00:00')
|
||||
plan_ids = self.env['sf.production.plan'].sudo().search([('date_planned_start', '>=', date_planned_start),
|
||||
('date_planned_start', '<',
|
||||
date_planned_end), ('state', '!=', 'draft')])
|
||||
|
||||
if plan_ids:
|
||||
sum_qty = sum([p.product_qty for p in plan_ids])
|
||||
if sum_qty >= self.single_machine_capacity:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class ResWorkcenterProductivity(models.Model):
|
||||
_inherit = 'mrp.workcenter.productivity'
|
||||
|
||||
@@ -1100,7 +1100,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
[('barcode', 'ilike', 'VL-SPOC')]).id),
|
||||
('origin', '=', self.production_id.name)])
|
||||
if move_out.state != 'done':
|
||||
move_out.write({'state': 'assigned'})
|
||||
move_out.write({'state': 'assigned', 'production_id': False})
|
||||
self.env['stock.move.line'].create(move_out.get_move_line(self.production_id, self))
|
||||
|
||||
# move_out._action_assign()
|
||||
@@ -1340,7 +1340,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
'name': 'button_delivery',
|
||||
'type': 'object',
|
||||
'string': '解除装夹',
|
||||
'class': 'btn-primary',
|
||||
'class': 'btn-primary jikimo_button_confirm',
|
||||
# 'className': 'btn-primary',
|
||||
'modifiers': '{"force_show": 1}'
|
||||
})
|
||||
@@ -1355,6 +1355,7 @@ class ResMrpWorkOrder(models.Model):
|
||||
workorder_ids = []
|
||||
delivery_type = '运送空料架'
|
||||
max_num = 4 # 最大配送数量
|
||||
feeder_station_start_id = False
|
||||
if len(self) > max_num:
|
||||
raise UserError('仅限于拆卸1-4个制造订单,请重新选择')
|
||||
for item in self:
|
||||
@@ -1363,6 +1364,10 @@ class ResMrpWorkOrder(models.Model):
|
||||
|
||||
production_ids.append(item.production_id.id)
|
||||
workorder_ids.append(item.id)
|
||||
if not feeder_station_start_id:
|
||||
down_product_agv_scheduling = self.get_down_product_agv_scheduling()
|
||||
if down_product_agv_scheduling:
|
||||
feeder_station_start_id = down_product_agv_scheduling.end_site_id.id
|
||||
return {
|
||||
'name': _('确认'),
|
||||
'type': 'ir.actions.act_window',
|
||||
@@ -1375,7 +1380,8 @@ class ResMrpWorkOrder(models.Model):
|
||||
'default_delivery_type': delivery_type,
|
||||
'default_workorder_ids': [(6, 0, workorder_ids)],
|
||||
'default_workcenter_id': self.env.context.get('default_workcenter_id'),
|
||||
'default_confirm_button': '确认解除'
|
||||
'default_confirm_button': '确认解除',
|
||||
'default_feeder_station_start_id': feeder_station_start_id,
|
||||
}}
|
||||
|
||||
|
||||
|
||||
@@ -574,6 +574,7 @@ class StockPicking(models.Model):
|
||||
('origin', '=', self.origin), ('picking_id', '=', self.id)])
|
||||
if self.location_id == move_in.location_id and self.location_dest_id == move_in.location_dest_id:
|
||||
if move_out.origin == move_in.origin:
|
||||
move_in.write({'production_id': False})
|
||||
if move_out.picking_id.state != 'done':
|
||||
raise UserError(
|
||||
_('该入库单对应的单号为%s的出库单还未完成,不能进行验证操作!' % move_out.picking_id.name))
|
||||
@@ -659,6 +660,11 @@ class ReStockMove(models.Model):
|
||||
return move_values
|
||||
|
||||
def _get_new_picking_values_Res(self, item, sorted_workorders, rescode):
|
||||
picking_type_id = self.mapped('picking_type_id').id
|
||||
if rescode == 'WH/OCOUT/':
|
||||
picking_type_id = self.env.ref('sf_manufacturing.outcontract_picking_out').id
|
||||
elif rescode == 'WH/OCIN/':
|
||||
picking_type_id = self.env.ref('sf_manufacturing.outcontract_picking_in').id
|
||||
return {
|
||||
'name': self.env['stock.picking']._get_name_Res(rescode),
|
||||
'origin': item.name,
|
||||
@@ -667,7 +673,7 @@ class ReStockMove(models.Model):
|
||||
'user_id': False,
|
||||
'move_type': self.mapped('group_id').move_type or 'direct',
|
||||
'partner_id': sorted_workorders.supplier_id.id,
|
||||
'picking_type_id': self.mapped('picking_type_id').id,
|
||||
'picking_type_id': picking_type_id,
|
||||
'location_id': self.mapped('location_id').id,
|
||||
'location_dest_id': self.mapped('location_dest_id').id,
|
||||
'state': 'confirmed',
|
||||
|
||||
@@ -10,7 +10,7 @@ odoo.define('sf_manufacturing.action_dispatch_confirm', function (require) {
|
||||
title: "确认",
|
||||
$content: $('<div>').append("请确认是否仅配送" + params.workorder_count + "个工件?"),
|
||||
buttons: [
|
||||
{ text: "确认", classes: 'btn-primary', close: true, click: () => dispatchConfirmed(parent, params) },
|
||||
{ text: "确认", classes: 'btn-primary jikimo_button_confirm', close: true, click: () => dispatchConfirmed(parent, params) },
|
||||
{ text: "取消", close: true },
|
||||
],
|
||||
});
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
<field name="model">mrp.production</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_production_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- <button name="action_cancel" position="before"> -->
|
||||
<!-- <button name="button_maintenance_req" type="object" string="维修请求"/> -->
|
||||
<!-- </button> -->
|
||||
<!-- <button name="action_cancel" position="before"> -->
|
||||
<!-- <button name="button_maintenance_req" type="object" string="维修请求"/> -->
|
||||
<!-- </button> -->
|
||||
<div name="button_box" position="inside">
|
||||
<button name="open_maintenance_request_mo" type="object" class="oe_stat_button" icon="fa-wrench"
|
||||
attrs="{'invisible': [('maintenance_count', '=', 0)]}"
|
||||
@@ -25,38 +25,81 @@
|
||||
</record>
|
||||
|
||||
<record id="custom_model_form_view_inherit" model="ir.ui.view">
|
||||
<field name="name">custom.model.form.view.inherit</field>
|
||||
<field name="model">mrp.workcenter</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_workcenter_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr='//form//sheet' position="after">
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids"/>
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<field name="name">custom.model.form.view.inherit</field>
|
||||
<field name="model">mrp.workcenter</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_workcenter_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr='//form//sheet' position="after">
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids"/>
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='default_capacity'][last()]" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='default_capacity'][last()]" position="after">
|
||||
|
||||
<record id="mrp_workcenter_view_kanban_inherit_workorder" model="ir.ui.view">
|
||||
<field name="name">mrp.workcenter.view.kanban.inherit.mrp.workorder</field>
|
||||
<field name="model">mrp.workcenter</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_workcenter_kanban"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- Desktop view -->
|
||||
<xpath expr='(//field[@name="name"])[1]' position="after">
|
||||
<field name="equipment_status" />
|
||||
<field name="equipment_image" />
|
||||
</xpath>
|
||||
<xpath expr='(//field[@name="name"])[2]' position="after">
|
||||
<field name="equipment_status" />
|
||||
<field name="equipment_image" widget="image" />
|
||||
</xpath>
|
||||
<xpath expr='(//a[@name="unblock"])' position="after">
|
||||
<div class="czyg">绿色:正常,红色:故障,黄色:下线/暂停</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<label for="default_capacity"/>
|
||||
<div class="o_row">
|
||||
<field name="default_capacity" string="产线日产能"/>
|
||||
台
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='default_capacity'][last()]" position="before">
|
||||
<label for="available_machine_number"/>
|
||||
<div class="o_row">
|
||||
<field name="available_machine_number"/>
|
||||
台
|
||||
</div>
|
||||
<label for="single_machine_capacity"/>
|
||||
<div class="o_row">
|
||||
<field name="single_machine_capacity"/>
|
||||
件/小时
|
||||
</div>
|
||||
<label for="production_line_hour_capacity"/>
|
||||
<div class="o_row">
|
||||
<field name="production_line_hour_capacity"/>
|
||||
件/小时
|
||||
</div>
|
||||
<label for="effective_working_hours_day"/>
|
||||
<div class="o_row">
|
||||
<field name="effective_working_hours_day"/>
|
||||
小时
|
||||
</div>
|
||||
</xpath>
|
||||
<!-- <xpath expr='//group[@name="capacity"]//field[@name="default_capacity"])' position="after">-->
|
||||
<!-- <group>-->
|
||||
<!-- <field name="available_machine_number"/>-->
|
||||
<!-- <field name="single_machine_capacity"/>-->
|
||||
<!-- <field name="production_line_hour_capacity"/>-->
|
||||
<!-- <field name="effective_working_hours_day"/>-->
|
||||
|
||||
<!-- </group>-->
|
||||
<!-- </xpath>-->
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mrp_workcenter_view_kanban_inherit_workorder" model="ir.ui.view">
|
||||
<field name="name">mrp.workcenter.view.kanban.inherit.mrp.workorder</field>
|
||||
<field name="model">mrp.workcenter</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_workcenter_kanban"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- Desktop view -->
|
||||
<xpath expr='(//field[@name="name"])[1]' position="after">
|
||||
<field name="equipment_status"/>
|
||||
<field name="equipment_image"/>
|
||||
</xpath>
|
||||
<xpath expr='(//field[@name="name"])[2]' position="after">
|
||||
<field name="equipment_status"/>
|
||||
<field name="equipment_image" widget="image"/>
|
||||
</xpath>
|
||||
<xpath expr='(//a[@name="unblock"])' position="after">
|
||||
<div class="czyg">绿色:正常,红色:故障,黄色:下线/暂停</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mrp_workcenter_view_kanban_inherit_workorder" model="ir.ui.view">
|
||||
<field name="name">mrp.workcenter.view.kanban.inherit.mrp.workorder</field>
|
||||
@@ -73,11 +116,11 @@
|
||||
|
||||
</xpath>
|
||||
<xpath expr='(//a[@name="unblock"])' position="after">
|
||||
<!-- <div class="czyg">绿色:正常,红色:故障,黄色:下线/暂停</div>-->
|
||||
<!-- <div class="czyg">绿色:正常,红色:故障,黄色:下线/暂停</div>-->
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
<!-------------------->
|
||||
<!-------------------->
|
||||
<record id="mrp_workcenter_kanban_inherit1" model="ir.ui.view">
|
||||
<field name="name">mrp.workcenter.kanban.inherit</field>
|
||||
<field name="model">mrp.workcenter</field>
|
||||
@@ -99,7 +142,7 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 继承原有的看板视图 -->
|
||||
<!-- 继承原有的看板视图 -->
|
||||
<record id="mrp_workcenter_kanban_inherit1" model="ir.ui.view">
|
||||
<field name="name">mrp.workcenter.kanban.inherit</field>
|
||||
<field name="model">mrp.workcenter</field>
|
||||
@@ -391,9 +434,9 @@
|
||||
<field name="model">mrp.production</field>
|
||||
<field name="inherit_id" ref="mrp.mrp_production_form_view"/>
|
||||
<field name="arch" type="xml">
|
||||
<!-- <button name="action_cancel" position="before"> -->
|
||||
<!-- <button name="button_maintenance_req" type="object" string="维修请求"/> -->
|
||||
<!-- </button> -->
|
||||
<!-- <button name="action_cancel" position="before"> -->
|
||||
<!-- <button name="button_maintenance_req" type="object" string="维修请求"/> -->
|
||||
<!-- </button> -->
|
||||
<div name="button_box" position="inside">
|
||||
<button name="open_maintenance_request_mo" type="object" class="oe_stat_button" icon="fa-wrench"
|
||||
attrs="{'invisible': [('maintenance_count', '=', 0)]}"
|
||||
|
||||
@@ -659,9 +659,9 @@
|
||||
<field name="name">工件配送</field>
|
||||
<field name="model">sf.workpiece.delivery</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="工件配送" class="center" create="0" delete="0">
|
||||
<tree string="工件配送" class="center" create="0" delete="0" js_class="remove_focus_list_view">
|
||||
<header>
|
||||
<button name="button_delivery" type="object" string="工件配送" class="btn-primary" attrs="{'force_show':1}"/>
|
||||
<button name="button_delivery" type="object" string="工件配送" class="btn-primary jikimo_button_confirm" attrs="{'force_show':1}"/>
|
||||
</header>
|
||||
<field name="status" widget="badge"
|
||||
decoration-success="status == '已配送'"
|
||||
|
||||
@@ -25,6 +25,7 @@ class ReworkWizard(models.TransientModel):
|
||||
processing_panel_id = fields.Many2many('sf.processing.panel', string="加工面")
|
||||
is_reprogramming = fields.Boolean(string='申请重新编程', default=False)
|
||||
is_reprogramming_readonly = fields.Boolean(string='申请重新编程(只读)', default=False)
|
||||
is_clamp_measure = fields.Boolean(string='保留装夹测量数据', default=True)
|
||||
reprogramming_num = fields.Integer('重新编程次数', default=0)
|
||||
programming_state = fields.Selection(
|
||||
[('待编程', '待编程'), ('编程中', '编程中'), ('已编程', '已编程'), ('已编程未下发', '已编程未下发'),
|
||||
@@ -35,6 +36,7 @@ class ReworkWizard(models.TransientModel):
|
||||
|
||||
def confirm(self):
|
||||
if self.routing_type in ['装夹预调', 'CNC加工']:
|
||||
self.is_clamp_measure = False
|
||||
self.workorder_id.is_rework = True
|
||||
self.production_id.write({'detection_result_ids': [(0, 0, {
|
||||
'rework_reason': self.rework_reason,
|
||||
@@ -58,19 +60,15 @@ class ReworkWizard(models.TransientModel):
|
||||
if processing_panels_missing:
|
||||
processing_panels_str = ','.join(processing_panels_missing)
|
||||
raise UserError('您还有待处理的检测结果中为%s的加工面未选择' % processing_panels_str)
|
||||
# processing_panels = set()
|
||||
# for handle_item in handle_result:
|
||||
# for dr_panel in self.processing_panel_id:
|
||||
# if dr_panel.name == handle_item.processing_panel:
|
||||
# processing_panels.add(dr_panel.name)
|
||||
# if len(processing_panels) != len(handle_result):
|
||||
# processing_panels_str = ','.join(processing_panels)
|
||||
# return UserError(f'您还有待处理的检测结果中为{processing_panels_str}的加工面未选择')
|
||||
for panel in self.processing_panel_id:
|
||||
panel_workorder = self.production_id.workorder_ids.filtered(
|
||||
lambda ap: ap.processing_panel == panel.name and ap.state != 'rework')
|
||||
if panel_workorder:
|
||||
panel_workorder.write({'state': 'rework'})
|
||||
rework_clamp_workorder = max(panel_workorder.filtered(
|
||||
lambda
|
||||
rp: rp.processing_panel == panel.name and rp.routing_type == '装夹预调' and rp.state in [
|
||||
'done', 'rework']))
|
||||
# panel_workorder.filtered(
|
||||
# lambda wo: wo.routing_type == '装夹预调').workpiece_delivery_ids.filtered(
|
||||
# lambda wd: wd.status == '待下发').write({'status': '已取消'})
|
||||
@@ -93,6 +91,43 @@ class ReworkWizard(models.TransientModel):
|
||||
self.production_id.detection_result_ids.filtered(
|
||||
lambda ap1: ap1.processing_panel == panel.name and ap1.handle_result == '待处理').write(
|
||||
{'handle_result': '已处理'})
|
||||
new_pre_workorder = self.production_id.workorder_ids.filtered(lambda
|
||||
p: p.routing_type == '装夹预调' and p.processing_panel == panel.name and p.state not in (
|
||||
'rework', 'done'))
|
||||
if new_pre_workorder and rework_clamp_workorder and self.is_clamp_measure is True:
|
||||
new_pre_workorder.write(
|
||||
{'X1_axis': rework_clamp_workorder.X1_axis, 'Y1_axis': rework_clamp_workorder.Y1_axis
|
||||
, 'Z1_axis': rework_clamp_workorder.Z1_axis,
|
||||
'X2_axis': rework_clamp_workorder.X2_axis
|
||||
, 'Y2_axis': rework_clamp_workorder.Y2_axis,
|
||||
'Z2_axis': rework_clamp_workorder.Z2_axis
|
||||
, 'X3_axis': rework_clamp_workorder.X3_axis,
|
||||
'Y3_axis': rework_clamp_workorder.Y3_axis
|
||||
, 'Z3_axis': rework_clamp_workorder.Z3_axis,
|
||||
'X4_axis': rework_clamp_workorder.X4_axis
|
||||
, 'Y4_axis': rework_clamp_workorder.Y4_axis,
|
||||
'Z4_axis': rework_clamp_workorder.Z4_axis
|
||||
, 'X5_axis': rework_clamp_workorder.X5_axis,
|
||||
'Y5_axis': rework_clamp_workorder.Y5_axis
|
||||
, 'Z5_axis': rework_clamp_workorder.Z5_axis,
|
||||
'X6_axis': rework_clamp_workorder.X6_axis
|
||||
, 'Y6_axis': rework_clamp_workorder.Y6_axis,
|
||||
'Z6_axis': rework_clamp_workorder.Z6_axis
|
||||
, 'X7_axis': rework_clamp_workorder.X7_axis,
|
||||
'Y7_axis': rework_clamp_workorder.Y7_axis
|
||||
, 'Z7_axis': rework_clamp_workorder.Z7_axis,
|
||||
'X8_axis': rework_clamp_workorder.X8_axis
|
||||
, 'Y8_axis': rework_clamp_workorder.Y8_axis,
|
||||
'Z8_axis': rework_clamp_workorder.Z8_axis
|
||||
, 'X9_axis': rework_clamp_workorder.X9_axis,
|
||||
'Y9_axis': rework_clamp_workorder.Y9_axis
|
||||
, 'Z9_axis': rework_clamp_workorder.Z9_axis,
|
||||
'X10_axis': rework_clamp_workorder.X10_axis
|
||||
, 'Y10_axis': rework_clamp_workorder.Y10_axis,
|
||||
'Z10_axis': rework_clamp_workorder.Z10_axis
|
||||
, 'X_deviation_angle': rework_clamp_workorder.X_deviation_angle,
|
||||
'material_center_point': rework_clamp_workorder.material_center_point
|
||||
})
|
||||
if self.is_reprogramming is False:
|
||||
if self.programming_state in ['已编程', '已下发']:
|
||||
if self.reprogramming_num >= 1 and self.programming_state == '已编程':
|
||||
@@ -149,9 +184,7 @@ class ReworkWizard(models.TransientModel):
|
||||
'cmm_ids': new_cnc_workorder.cmm_ids.sudo()._json_cmm_program(panel.name,
|
||||
ret),
|
||||
'cnc_worksheet': cnc_rework.cnc_worksheet})
|
||||
new_pre_workorder = self.production_id.workorder_ids.filtered(lambda
|
||||
p: p.routing_type == '装夹预调' and p.processing_panel == panel.name and p.state not in (
|
||||
'rework', 'done'))
|
||||
|
||||
if new_pre_workorder:
|
||||
pre_rework = max(self.production_id.workorder_ids.filtered(
|
||||
lambda pr: pr.processing_panel == panel.name and pr.state in (
|
||||
|
||||
@@ -14,17 +14,25 @@
|
||||
<group>
|
||||
<field name="processing_panel_id" options="{'no_create': True}"
|
||||
attrs='{"invisible": [("routing_type","=","装夹预调")]}' widget="many2many_tags"/>
|
||||
|
||||
</group>
|
||||
<div attrs='{"invisible": [("routing_type","=","装夹预调")]}'>
|
||||
<span style='font-weight:bold;'>保留装夹测量数据
|
||||
<field name="is_clamp_measure" force_save="1"/>
|
||||
</span>
|
||||
</div>
|
||||
<div attrs='{"invisible": [("reprogramming_num","=",0)]}'>
|
||||
注意: 该制造订单产品已申请重新编程次数为<field
|
||||
name="reprogramming_num" string=""
|
||||
readonly="1"
|
||||
style='color:red;'/>,且当前编程状态为
|
||||
<field name="programming_state" string=""
|
||||
decoration-info="programming_state == '待编程'"
|
||||
decoration-success="programming_state == '已下发'"
|
||||
decoration-warning="programming_state =='编程中'"
|
||||
decoration-danger="programming_state =='已编程'" readonly="1"/>
|
||||
<span style='font-weight:bold;'>
|
||||
注意: 该制造订单产品已申请重新编程次数为<field
|
||||
name="reprogramming_num" string=""
|
||||
readonly="1"
|
||||
style='color:red;'/>,且当前编程状态为
|
||||
<field name="programming_state" string=""
|
||||
decoration-info="programming_state == '待编程'"
|
||||
decoration-success="programming_state == '已下发'"
|
||||
decoration-warning="programming_state =='编程中'"
|
||||
decoration-danger="programming_state =='已编程'" readonly="1"/>
|
||||
</span>
|
||||
</div>
|
||||
<div attrs='{"invisible": ["|",("routing_type","in",["装夹预调","CNC加工"]),("programming_state","not in",["已下发"])],"readonly": [("tool_state", "=", "2")]}'>
|
||||
<span style='font-weight:bold;'>申请重新编程
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<field name="name">sf.workpiece.delivery.wizard.form.view</field>
|
||||
<field name="model">sf.workpiece.delivery.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form js_class="remove_focus_view">
|
||||
<form js_class="remove_focus_form_view">
|
||||
<sheet>
|
||||
<field name="delivery_ids" invisible="True"/>
|
||||
<field name="workorder_ids" invisible="True"/>
|
||||
@@ -18,8 +18,8 @@
|
||||
<field name="workcenter_id" options="{'no_create': True}"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button string="确认配送" name="dispatch_confirm" type="object" class="oe_highlight o_wizard_confirm_button" attrs="{'invisible': [('confirm_button', '!=', '确认配送')]}"/>
|
||||
<button string="确认解除" name="dispatch_confirm" type="object" class="oe_highlight o_wizard_confirm_button" attrs="{'invisible': [('confirm_button', '!=', '确认解除')]}"/>
|
||||
<button string="确认配送" name="dispatch_confirm" type="object" class="oe_highlight o_wizard_confirm_button jikimo_button_confirm" attrs="{'invisible': [('confirm_button', '!=', '确认配送')]}"/>
|
||||
<button string="确认解除" name="dispatch_confirm" type="object" class="oe_highlight o_wizard_confirm_button jikimo_button_confirm" attrs="{'invisible': [('confirm_button', '!=', '确认解除')]}"/>
|
||||
<button string="取消" class="btn btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</sheet>
|
||||
|
||||
@@ -180,6 +180,13 @@ class WorkpieceDeliveryWizard(models.TransientModel):
|
||||
self.feeder_station_destination_id = self.route_id.end_site_id.id
|
||||
|
||||
def on_barcode_scanned(self, barcode):
|
||||
# 判断barcode是否是数字
|
||||
if not barcode.isdigit():
|
||||
# 判断是否是AGV接驳站名称
|
||||
agv_site = self.env['sf.agv.site'].search([('name', '=', barcode)])
|
||||
if agv_site:
|
||||
self.feeder_station_start_id = agv_site.id
|
||||
return
|
||||
delivery_type = self.env.context.get('default_delivery_type')
|
||||
if delivery_type == '上产线':
|
||||
workorder = self.env['mrp.workorder'].search(
|
||||
@@ -203,6 +210,13 @@ class WorkpieceDeliveryWizard(models.TransientModel):
|
||||
# 将对象添加到对应的同模型且是多对多类型里
|
||||
self.production_ids |= workorder.production_id
|
||||
self.workorder_ids |= workorder
|
||||
|
||||
if not self.feeder_station_start_id:
|
||||
down_product_agv_scheduling = self.get_down_product_agv_scheduling()
|
||||
if down_product_agv_scheduling:
|
||||
self.feeder_station_start_id = down_product_agv_scheduling.end_site_id.id
|
||||
else:
|
||||
raise UserError('该rfid码对应的工单不存在')
|
||||
return
|
||||
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ class sf_production_plan(models.Model):
|
||||
|
||||
return num
|
||||
|
||||
def do_production_schedule(self):
|
||||
def do_production_schedule(self, date_planned_start):
|
||||
"""
|
||||
排程方法
|
||||
"""
|
||||
@@ -199,6 +199,10 @@ class sf_production_plan(models.Model):
|
||||
if not record.production_line_id:
|
||||
raise ValidationError("未选择生产线")
|
||||
else:
|
||||
|
||||
is_schedule = self.deal_processing_schedule(date_planned_start)
|
||||
if not is_schedule:
|
||||
raise ValidationError("排程失败")
|
||||
workorder_id_list = record.production_id.workorder_ids.ids
|
||||
if record.production_id:
|
||||
if record.production_id.workorder_ids:
|
||||
@@ -249,6 +253,26 @@ class sf_production_plan(models.Model):
|
||||
'target': 'current', # 跳转的目标窗口,可以是'current'或'new'
|
||||
}
|
||||
|
||||
# 处理是否可排程
|
||||
def deal_processing_schedule(self, date_planned_start):
|
||||
for record in self:
|
||||
workcenter_ids = record.production_line_id.mrp_workcenter_ids
|
||||
if not workcenter_ids:
|
||||
raise UserError('生产线没有配置工作中心')
|
||||
production_lines = workcenter_ids.filtered(lambda b: "自动生产线" in b.name)
|
||||
if not production_lines: # 判断是否配置了自动生产线
|
||||
raise UserError('生产线没有配置自动生产线')
|
||||
if date_planned_start < datetime.now(): # 判断计划开始时间是否小于当前时间
|
||||
raise UserError('计划开始时间不能小于当前时间')
|
||||
if all(not production_line.deal_with_workcenter_calendar(date_planned_start) for production_line in
|
||||
production_lines): # 判断计划开始时间是否在配置的工作中心的工作日历内
|
||||
raise UserError('当前计划开始时间不能预约排程,请在工作时间内排程')
|
||||
if not production_lines.deal_available_default_capacity(date_planned_start): # 判断生产线是否可排程
|
||||
raise UserError('当前计划开始时间不能预约排程,生产线今日没有可排程的资源')
|
||||
if not production_lines.deal_available_single_machine_capacity(date_planned_start): # 判断生产线是否可排程
|
||||
raise UserError('当前计划开始时间不能预约排程,生产线该时间段没有可排程的资源')
|
||||
return True
|
||||
|
||||
def calculate_plan_time_before(self, item, workorder_id_list):
|
||||
"""
|
||||
根据CNC工单的时间去计算之前的其他工单的开始结束时间
|
||||
|
||||
@@ -36,7 +36,7 @@ class Action_Plan_All_Wizard(models.TransientModel):
|
||||
plan_obj = self.env['sf.production.plan'].browse(plan.id)
|
||||
plan_obj.production_line_id = self.production_line_id.id
|
||||
plan.date_planned_start = self.date_planned_start
|
||||
plan_obj.do_production_schedule()
|
||||
plan_obj.do_production_schedule(self.date_planned_start)
|
||||
# plan_obj.state = 'done'
|
||||
print('处理计划:', plan.id, '完成')
|
||||
|
||||
|
||||
4
sf_stock/__init__.py
Normal file
4
sf_stock/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
from . import models
|
||||
36
sf_stock/__manifest__.py
Normal file
36
sf_stock/__manifest__.py
Normal file
@@ -0,0 +1,36 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
{
|
||||
'name': "sf_stock",
|
||||
|
||||
'summary': """
|
||||
Short (1 phrase/line) summary of the module's purpose, used as
|
||||
subtitle on modules listing or apps.openerp.com""",
|
||||
|
||||
'description': """
|
||||
Long description of module's purpose
|
||||
""",
|
||||
|
||||
'author': "My Company",
|
||||
'website': "https://www.yourcompany.com",
|
||||
|
||||
# Categories can be used to filter modules in modules listing
|
||||
# Check https://github.com/odoo/odoo/blob/16.0/odoo/addons/base/data/ir_module_category_data.xml
|
||||
# for the full list
|
||||
'category': 'Uncategorized',
|
||||
'version': '0.1',
|
||||
|
||||
# any module necessary for this one to work correctly
|
||||
'depends': ['sf_sale', 'stock'],
|
||||
|
||||
# always loaded
|
||||
'data': [
|
||||
# 'security/ir.model.access.csv',
|
||||
'views/stock_picking.xml',
|
||||
],
|
||||
# only loaded in demonstration mode
|
||||
'demo': [
|
||||
'demo/demo.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
}
|
||||
3
sf_stock/controllers/__init__.py
Normal file
3
sf_stock/controllers/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import controllers
|
||||
21
sf_stock/controllers/controllers.py
Normal file
21
sf_stock/controllers/controllers.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# from odoo import http
|
||||
|
||||
|
||||
# class SfStock(http.Controller):
|
||||
# @http.route('/sf_stock/sf_stock', auth='public')
|
||||
# def index(self, **kw):
|
||||
# return "Hello, world"
|
||||
|
||||
# @http.route('/sf_stock/sf_stock/objects', auth='public')
|
||||
# def list(self, **kw):
|
||||
# return http.request.render('sf_stock.listing', {
|
||||
# 'root': '/sf_stock/sf_stock',
|
||||
# 'objects': http.request.env['sf_stock.sf_stock'].search([]),
|
||||
# })
|
||||
|
||||
# @http.route('/sf_stock/sf_stock/objects/<model("sf_stock.sf_stock"):obj>', auth='public')
|
||||
# def object(self, obj, **kw):
|
||||
# return http.request.render('sf_stock.object', {
|
||||
# 'object': obj
|
||||
# })
|
||||
30
sf_stock/demo/demo.xml
Normal file
30
sf_stock/demo/demo.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<!--
|
||||
<record id="object0" model="sf_stock.sf_stock">
|
||||
<field name="name">Object 0</field>
|
||||
<field name="value">0</field>
|
||||
</record>
|
||||
|
||||
<record id="object1" model="sf_stock.sf_stock">
|
||||
<field name="name">Object 1</field>
|
||||
<field name="value">10</field>
|
||||
</record>
|
||||
|
||||
<record id="object2" model="sf_stock.sf_stock">
|
||||
<field name="name">Object 2</field>
|
||||
<field name="value">20</field>
|
||||
</record>
|
||||
|
||||
<record id="object3" model="sf_stock.sf_stock">
|
||||
<field name="name">Object 3</field>
|
||||
<field name="value">30</field>
|
||||
</record>
|
||||
|
||||
<record id="object4" model="sf_stock.sf_stock">
|
||||
<field name="name">Object 4</field>
|
||||
<field name="value">40</field>
|
||||
</record>
|
||||
-->
|
||||
</data>
|
||||
</odoo>
|
||||
3
sf_stock/models/__init__.py
Normal file
3
sf_stock/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import stock_picking
|
||||
110
sf_stock/models/stock_picking.py
Normal file
110
sf_stock/models/stock_picking.py
Normal file
@@ -0,0 +1,110 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import requests
|
||||
from odoo import models, fields, api
|
||||
|
||||
from odoo.exceptions import UserError
|
||||
import logging
|
||||
from odoo.tools import date_utils
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class StockPicking(models.Model):
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
cancel_backorder_ids = fields.Boolean(default=False, string='是否取消后置单据')
|
||||
|
||||
# 重写验证,下发发货到bfm
|
||||
def button_validate(self):
|
||||
info = super(StockPicking, self).button_validate()
|
||||
if self.picking_type_code == 'outgoing':
|
||||
self.send_to_bfm()
|
||||
return info
|
||||
|
||||
def deal_move_ids(self, send_move_ids, send_move_line_ids):
|
||||
move_ids = [] # 本次发货单
|
||||
move_line_ids = [] # 本次发货单行
|
||||
if send_move_ids:
|
||||
for item in send_move_ids:
|
||||
val = {
|
||||
'name': item.product_id.upload_model_file.display_name,
|
||||
'quantity_done': item.quantity_done,
|
||||
'date': date_utils.json_default(item.date) if item.date else None,
|
||||
'description_picking': item.description_picking,
|
||||
'date_deadline': date_utils.json_default(item.date_deadline) if item.date_deadline else None,
|
||||
'product_uom_qty': item.product_uom_qty,
|
||||
'sequence': item.sequence,
|
||||
'price_unit': item.price_unit,
|
||||
'priority': item.priority,
|
||||
'state': item.state,
|
||||
}
|
||||
move_ids.append(val)
|
||||
for item in send_move_line_ids:
|
||||
val = {
|
||||
'qty_done': item.qty_done,
|
||||
'reserved_qty': item.reserved_qty,
|
||||
'reserved_uom_qty': item.reserved_uom_qty,
|
||||
'date': date_utils.json_default(item.date) if item.date else None,
|
||||
'description_picking': item.description_picking,
|
||||
'state': item.state,
|
||||
}
|
||||
move_line_ids.append(val)
|
||||
return move_ids, move_line_ids
|
||||
|
||||
def deal_send_backorder_id(self, backorder_ids1):
|
||||
backorder_ids = []
|
||||
|
||||
if backorder_ids1:
|
||||
for item in backorder_ids1:
|
||||
move_ids, move_line_ids = self.deal_move_ids(item.move_ids, item.move_line_ids)
|
||||
val = {
|
||||
'receiverName': item.receiverName,
|
||||
'name': item.sale_id.default_code,
|
||||
'send_no': item.name,
|
||||
'scheduled_date': date_utils.json_default(item.scheduled_date) if item.scheduled_date else None,
|
||||
'date': date_utils.json_default(item.date) if item.date else None,
|
||||
'date_deadline': date_utils.json_default(item.date_deadline) if item.date_deadline else None,
|
||||
'date_done': date_utils.json_default(item.date_done) if item.date_done else None,
|
||||
'move_ids': move_ids,
|
||||
'move_line_ids': move_line_ids,
|
||||
'state': item.state,
|
||||
'move_type': item.move_type,
|
||||
}
|
||||
backorder_ids.append(val)
|
||||
return backorder_ids
|
||||
|
||||
def send_to_bfm(self):
|
||||
skip_backorder = self.env.context.get('skip_backorder')
|
||||
# 下发发货到bfm
|
||||
config = self.env['res.config.settings'].get_values()
|
||||
move_ids, move_line_ids = self.deal_move_ids(self.move_ids, self.move_line_ids)
|
||||
data = {
|
||||
'params': {
|
||||
'receiverName': self.receiverName,
|
||||
'priority': self.priority,
|
||||
'name': self.sale_id.default_code,
|
||||
'send_no': self.name,
|
||||
'scheduled_date': date_utils.json_default(self.scheduled_date) if self.scheduled_date else None,
|
||||
'date': date_utils.json_default(self.date) if self.date else None,
|
||||
'date_deadline': date_utils.json_default(self.date_deadline) if self.date_deadline else None,
|
||||
'date_done': date_utils.json_default(self.date_done) if self.date_done else None,
|
||||
'move_ids': move_ids,
|
||||
'move_line_ids': move_line_ids,
|
||||
'state': self.state,
|
||||
'backorder_id': self.deal_send_backorder_id(self.backorder_id),
|
||||
'backorder_ids': self.deal_send_backorder_id(self.backorder_ids),
|
||||
'cancel_backorder_ids': skip_backorder,
|
||||
'move_type': self.move_type,
|
||||
},
|
||||
}
|
||||
url1 = config['bfm_url_new'] + '/api/stock/deliver_goods'
|
||||
json_str = json.dumps(data)
|
||||
print('json_str', json_str)
|
||||
r = requests.post(url1, json=data, data=None)
|
||||
if r.status_code == 200:
|
||||
result = json.loads(r.json()['result'])
|
||||
if result['code'] != 200:
|
||||
raise UserError(result['message'] or '工厂发货下发bfm失败')
|
||||
else:
|
||||
raise UserError('工厂发货下发bfm失败')
|
||||
2
sf_stock/security/ir.model.access.csv
Normal file
2
sf_stock/security/ir.model.access.csv
Normal file
@@ -0,0 +1,2 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_sf_stock_sf_stock,sf_stock.sf_stock,model_sf_stock_sf_stock,base.group_user,1,1,1,1
|
||||
|
5
sf_stock/views/stock_picking.xml
Normal file
5
sf_stock/views/stock_picking.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user