Merge branch 'master' into dev_zhou
# Conflicts: # yuthon_will_task/models/yuthon_will_task.py
This commit is contained in:
@@ -18,6 +18,6 @@ def exception_handle(func):
|
||||
return json.dumps({'data': '', 'code': 500, 'message': str(e)})
|
||||
except Exception as e:
|
||||
_logger.exception(e)
|
||||
return json.dumps({'data': '', 'code': 500, 'message': str(e)})
|
||||
return json.dumps({'data': '', 'code': 500, 'message': f'Server Error'})
|
||||
|
||||
return wrapper
|
||||
|
||||
@@ -128,3 +128,26 @@ class RestaurantController(Controller):
|
||||
}
|
||||
return json.dumps({'data': catering_data, 'code': 200, 'message': ''})
|
||||
return json.dumps({'data': '', 'code': 200, 'message': '没有查询到数据'})
|
||||
|
||||
@route('/phone/restaurant/get_catering_statistics', methods=['POST'], type='http', auth="none", csrf=False, cors='*')
|
||||
@exception_handle
|
||||
def get_catering_statistics(self, **kwargs):
|
||||
menu_date = kwargs.get('menu_date', date.today())
|
||||
|
||||
if not menu_date:
|
||||
return json.dumps({'data': '', 'code': 500, 'message': '参数错误'})
|
||||
|
||||
caterings = request.env['yuthon.catering'].sudo().search([
|
||||
('date', '=', menu_date),
|
||||
])
|
||||
catering_data = {
|
||||
'breakfast': 0,
|
||||
'lunch': 0,
|
||||
}
|
||||
if caterings:
|
||||
catering_data = {
|
||||
'breakfast': len(caterings.filtered(lambda x: x.breakfast)),
|
||||
'lunch': len(caterings.filtered(lambda x: x.lunch)),
|
||||
}
|
||||
return json.dumps({'data': catering_data, 'code': 200, 'message': ''})
|
||||
return json.dumps({'data': catering_data, 'code': 200, 'message': ''})
|
||||
|
||||
@@ -13,7 +13,7 @@ class IssueLibrary(models.Model):
|
||||
_rec_name = 'name'
|
||||
|
||||
name = fields.Char(string='议题名称')
|
||||
name_text = fields.Text(string='议题名称', compute='_compute_name', readonly=False)
|
||||
name_text = fields.Text(string='议题名称', compute='_compute_name', readonly=False, store=True)
|
||||
depart_opinions = fields.Text(string='部门意见')
|
||||
topic_content = fields.Text(string='事项背景')
|
||||
will_based = fields.Text(string='上会依据')
|
||||
@@ -840,6 +840,14 @@ class IssueLibrary(models.Model):
|
||||
for record in self:
|
||||
record.name_text = record.name
|
||||
|
||||
@api.model
|
||||
def name_search(self, name='', args=None, operator='ilike', limit=100):
|
||||
args = args or []
|
||||
domain = []
|
||||
if name:
|
||||
domain = ['|', '|', ('name', operator, name), ('code', operator, name), ('name_text', operator, name)]
|
||||
return self.search(domain + args, limit=limit).name_get()
|
||||
|
||||
@api.onchange('issue_library_room_line_ids')
|
||||
def _onchange_issue_library_room_line_ids(self):
|
||||
for record in self.issue_library_room_line_ids:
|
||||
|
||||
@@ -331,6 +331,8 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="议题">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="name_text"/>
|
||||
<filter string="待上会" name="issue_state" domain="[('issue_state', '=', 'draft')]"/>
|
||||
<filter string="已上会" name="issue_state" domain="[('issue_state', '=', 'on_meet')]"/>
|
||||
<filter string="待上会审批" name="to_approval" domain="[('issue_state', '=', 'to_approval')]"/>
|
||||
@@ -343,6 +345,8 @@
|
||||
</searchpanel>
|
||||
<header_search class="header-search-inline">
|
||||
<field name="name"/>
|
||||
<field name="code"/>
|
||||
<field name="name_text"/>
|
||||
<field name="department_id"/>
|
||||
</header_search>
|
||||
</search>
|
||||
|
||||
@@ -39,6 +39,7 @@ class InheritMaintenanceEquipment(models.Model):
|
||||
department_id1 = fields.Many2one('hr.department', string="部门", related='employee_id.department_id')
|
||||
is_jtbb = fields.Boolean(compute='_compute_company_id')
|
||||
mainten_state = fields.Selection([('ing', '使用中'), ('finish', '暂停')], string="设备状态")
|
||||
borrow_state = fields.Selection([('available', '可借'), ('borrowed', '已借出')], string="借用状态", default='available')
|
||||
equipment_number = fields.Char(string="数量")
|
||||
equipment_unit = fields.Many2one('uom.uom', string="单位")
|
||||
purchase_date = fields.Date(string="购入日期")
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
|
||||
<xpath expr="//field[@name='name']" position="after">
|
||||
<field name="mainten_state"/>
|
||||
<field name="borrow_state"/>
|
||||
<field name="model" string="型号"/>
|
||||
<field name="equipment_number"/>
|
||||
<field name="equipment_unit"/>
|
||||
|
||||
@@ -66,11 +66,13 @@ class BatchInvoiceExportWizard(models.TransientModel):
|
||||
|
||||
sheet['B2'] = f'{title_year}年{title_month}月'
|
||||
|
||||
# 统计最常见公司(用于C2)
|
||||
# 统计最常见公司(用于C2,取合同出租方公司简称)
|
||||
company_list = []
|
||||
for record in receivable_records:
|
||||
if record.contract_id and record.contract_id.company_id:
|
||||
company_list.append(record.contract_id.company_id.name)
|
||||
if record.contract_id and record.contract_id.lessor_id:
|
||||
referred = record.contract_id.lessor_id.referred
|
||||
if referred:
|
||||
company_list.append(referred)
|
||||
|
||||
if company_list:
|
||||
from collections import Counter
|
||||
@@ -80,6 +82,19 @@ class BatchInvoiceExportWizard(models.TransientModel):
|
||||
|
||||
sheet['C2'] = most_common_company
|
||||
|
||||
# 统计最常见权属类型(用于文件名)
|
||||
tenure_type_map = {'self': '自有', 'agency': '代管'}
|
||||
tenure_list = []
|
||||
for record in receivable_records:
|
||||
if record.contract_id and record.contract_id.tenure_type:
|
||||
tenure_list.append(record.contract_id.tenure_type)
|
||||
if tenure_list:
|
||||
from collections import Counter
|
||||
most_common_tenure = Counter(tenure_list).most_common(1)[0][0]
|
||||
tenure_type_label = tenure_type_map.get(most_common_tenure, most_common_tenure)
|
||||
else:
|
||||
tenure_type_label = ''
|
||||
|
||||
# 从第4行开始写入数据
|
||||
row = 4
|
||||
template_row_idx = 4
|
||||
@@ -182,7 +197,7 @@ class BatchInvoiceExportWizard(models.TransientModel):
|
||||
file_data = base64.b64encode(output.read()).decode('utf-8')
|
||||
|
||||
attachment = self.env['ir.attachment'].create({
|
||||
'name': f'批量开票_{title_year}年{title_month}月.xlsx',
|
||||
'name': f'{most_common_company}_{tenure_type_label}_{title_year}年{title_month}月批量开票.xlsx',
|
||||
'type': 'binary',
|
||||
'datas': file_data,
|
||||
'mimetype': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
from . import models
|
||||
from . import models
|
||||
from . import report
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
'category': 'zhou',
|
||||
'version': '0.1',
|
||||
'license': 'LGPL-3',
|
||||
'depends': ['base', 'soong_workflow', 'hr', 'product', 'documents', 'purchase', 'purchase_stock', 'sun_badge_menu', 'website_sale'],
|
||||
'depends': ['base', 'soong_workflow', 'hr', 'product', 'documents', 'purchase', 'purchase_stock', 'sun_badge_menu', 'website_sale', 'yuthon_stock'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'security/company_rule.xml',
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from . import purchase_order_print_report
|
||||
@@ -0,0 +1,15 @@
|
||||
from odoo import models
|
||||
|
||||
|
||||
class PurchaseOrderPrintReport(models.AbstractModel):
|
||||
_name = 'report.yuthon_purchase.purchase_order_print_template'
|
||||
_description = '采购审批单'
|
||||
|
||||
def _get_report_values(self, docids, data=None):
|
||||
docs = self.env['purchase.order'].sudo().browse(docids)
|
||||
|
||||
docargs = {
|
||||
'docs': docs,
|
||||
}
|
||||
|
||||
return docargs
|
||||
@@ -21,7 +21,6 @@
|
||||
.text-center { text-align: center !important; }
|
||||
.text-right { text-align: right !important; }
|
||||
</style>
|
||||
|
||||
<div class="po-wrap">
|
||||
<div class="po-title">采购审批单</div>
|
||||
|
||||
@@ -83,7 +82,6 @@
|
||||
<td width="8%">税项</td>
|
||||
<td width="8%">金额</td>
|
||||
</tr>
|
||||
|
||||
<t t-foreach="doc.order_line" t-as="line">
|
||||
<tr>
|
||||
<td><span t-esc="line.product_id.name or ''"/></td>
|
||||
|
||||
@@ -8,12 +8,17 @@
|
||||
'license': 'LGPL-3',
|
||||
'depends': ['base', 'stock', 'purchase_stock', 'documents', 'maintenance', 'funenc_json_widget'],
|
||||
'data': [
|
||||
'data/sequence.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/company_rule.xml',
|
||||
'wizard/stock_picking_transfer_wizard_views.xml',
|
||||
'wizard/putaway_rule_batch_update_wizard_views.xml',
|
||||
'wizard/stock_picking_submit_wizard_views.xml',
|
||||
'wizard/stock_picking_return_wizard_views.xml',
|
||||
'views/menu.xml',
|
||||
'views/inherit_stock_picking.xml',
|
||||
'views/Inherit_stock_picking_type_views.xml',
|
||||
'views/inherit_product_template_views.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="seq_borrow_workflow" model="ir.sequence">
|
||||
<field name="name">借用领用审批流水号</field>
|
||||
<field name="code">borrow.workflow.sequence</field>
|
||||
<field name="prefix">JY%(y)s%(month)s%(day)s</field>
|
||||
<field name="padding">4</field>
|
||||
<field name="company_id" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- 修复旧数据:给借用领用 workflow record 补上 workflow_id -->
|
||||
<function model="soong.workflow.record" name="fix_borrow_workflow_id"/>
|
||||
</odoo>
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
from . import inherit_stock_picking
|
||||
from . import Inherit_stock_picking_type
|
||||
from . import inherit_stock_location
|
||||
from . import inherit_stock_move
|
||||
from . import inherit_stock_move
|
||||
from . import ir_model_patch
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models
|
||||
@@ -27,4 +27,62 @@ class ProductProduct(models.Model):
|
||||
class StockMove(models.Model):
|
||||
_inherit = 'stock.move'
|
||||
|
||||
product_location_ids_display = fields.Char(
|
||||
string='所在仓库',
|
||||
compute='_compute_product_location_ids_display',
|
||||
store=False,
|
||||
)
|
||||
available_location_ids = fields.Many2many(
|
||||
'stock.location',
|
||||
string='可选位置',
|
||||
compute='_compute_available_location_ids',
|
||||
store=False,
|
||||
)
|
||||
borrow_type = fields.Selection(
|
||||
[('borrow', '借用'), ('requisition', '领用')],
|
||||
string='借用/领用',
|
||||
compute='_compute_borrow_type',
|
||||
store=False,
|
||||
)
|
||||
|
||||
@api.depends('product_id', 'product_id.product_tag_ids')
|
||||
def _compute_borrow_type(self):
|
||||
"""根据产品标签判断是借用还是领用"""
|
||||
for move in self:
|
||||
if move.product_id:
|
||||
tag_names = move.product_id.product_tag_ids.mapped('name')
|
||||
if '固定资产' in tag_names:
|
||||
move.borrow_type = 'borrow'
|
||||
else:
|
||||
move.borrow_type = 'requisition'
|
||||
else:
|
||||
move.borrow_type = False
|
||||
|
||||
@api.depends('product_id')
|
||||
def _compute_product_location_ids_display(self):
|
||||
"""根据产品查找所有有库存的仓库位置,拼接显示"""
|
||||
for move in self:
|
||||
if move.product_id:
|
||||
quants = self.env['stock.quant'].search([
|
||||
('product_id', '=', move.product_id.id),
|
||||
('quantity', '>', 0),
|
||||
('location_id.usage', '=', 'internal'),
|
||||
])
|
||||
locations = quants.mapped('location_id')
|
||||
move.product_location_ids_display = ' / '.join(locations.mapped('complete_name')) if locations else ''
|
||||
else:
|
||||
move.product_location_ids_display = ''
|
||||
|
||||
@api.depends('product_id')
|
||||
def _compute_available_location_ids(self):
|
||||
"""计算该产品有库存的位置,用于 location_id 的 domain 过滤"""
|
||||
for move in self:
|
||||
if move.product_id:
|
||||
quants = self.env['stock.quant'].search([
|
||||
('product_id', '=', move.product_id.id),
|
||||
('quantity', '>', 0),
|
||||
('location_id.usage', '=', 'internal'),
|
||||
])
|
||||
move.available_location_ids = quants.mapped('location_id')
|
||||
else:
|
||||
move.available_location_ids = self.env['stock.location']
|
||||
|
||||
@@ -28,6 +28,23 @@ class StockPicking(models.Model):
|
||||
acceptance_user_id = fields.Many2one('res.users', string="指定验收人")
|
||||
certifier_user_id = fields.Many2one('res.users', string="指定证明人")
|
||||
is_admin = fields.Boolean(string='是否管理员', compute='_compute_is_admin')
|
||||
approval_state = fields.Selection([
|
||||
('draft', '草稿'),
|
||||
('pending', '待审批'),
|
||||
('approved', '已批准'),
|
||||
('refused', '已拒绝'),
|
||||
], string='审批状态', default='draft')
|
||||
approver_id = fields.Many2one('res.users', string='审批人')
|
||||
borrower_id = fields.Many2one('res.users', string='借用人')
|
||||
return_state = fields.Selection([
|
||||
('none', '未申请'),
|
||||
('pending', '待审批'),
|
||||
('approved', '已归还'),
|
||||
('refused', '已拒绝'),
|
||||
], string='归还状态', default='none')
|
||||
return_approver_id = fields.Many2one('res.users', string='归还审批人')
|
||||
borrow_title = fields.Char(string='标题')
|
||||
borrow_code = fields.Char(string='流水号', compute='_compute_borrow_code', store=False)
|
||||
user_id = fields.Many2one('res.users', string="采购人员", compute="_compute_origin", store=True)
|
||||
receipt_status = fields.Selection([
|
||||
('acceptance', '验收'),
|
||||
@@ -155,22 +172,43 @@ class StockPicking(models.Model):
|
||||
self.certifier_id = current_user.id
|
||||
self.certification_date = odoo_fields.Datetime.now()
|
||||
|
||||
@api.depends('location_id', 'picking_type_code')
|
||||
@api.depends('picking_type_code', 'move_ids_without_package')
|
||||
def _compute_available_product_ids(self):
|
||||
"""根据仓库和作业类型计算可用产品列表"""
|
||||
"""根据作业类型计算可用产品列表"""
|
||||
for record in self:
|
||||
# 如果是入库单,返回所有产品(不限制)
|
||||
if record.picking_type_code == 'incoming':
|
||||
record.available_product_ids = self.env['product.product'].search([])
|
||||
# 如果是借用/领用等,只返回该仓库中有库存的产品
|
||||
elif record.location_id:
|
||||
elif record.picking_type_code == 'internal':
|
||||
# 当前单据已选产品(借出后库存可能为0,仍需可见)
|
||||
existing_products = record.move_ids_without_package.mapped('product_id')
|
||||
# 普通产品:有库存
|
||||
quants = self.env['stock.quant'].search([
|
||||
('location_id', '=', record.location_id.id),
|
||||
('quantity', '>', 0) # 只查询有库存的
|
||||
('quantity', '>', 0),
|
||||
('location_id.usage', '=', 'internal'),
|
||||
])
|
||||
stock_products = quants.mapped('product_id')
|
||||
# 固定资产产品:有可用设备
|
||||
available_equip = self.env['maintenance.equipment'].search([
|
||||
('borrow_state', '=', 'available'),
|
||||
])
|
||||
equip_product_names = set()
|
||||
for equip in available_equip:
|
||||
base_name = equip.name.rsplit('-', 1)[0] if '-' in equip.name else equip.name
|
||||
equip_product_names.add(base_name)
|
||||
if equip_product_names:
|
||||
fixed_asset_products = self.env['product.product'].search([
|
||||
('name', 'in', list(equip_product_names)),
|
||||
('product_tag_ids.name', 'in', ['固定资产']),
|
||||
])
|
||||
else:
|
||||
fixed_asset_products = self.env['product.product']
|
||||
record.available_product_ids = stock_products | fixed_asset_products | existing_products
|
||||
else:
|
||||
quants = self.env['stock.quant'].search([
|
||||
('quantity', '>', 0),
|
||||
('location_id.usage', '=', 'internal'),
|
||||
])
|
||||
record.available_product_ids = quants.mapped('product_id')
|
||||
else:
|
||||
record.available_product_ids = False
|
||||
|
||||
|
||||
@api.depends('picking_type_code')
|
||||
@@ -215,6 +253,55 @@ class StockPicking(models.Model):
|
||||
})
|
||||
self.location_dest_id = location_dest_id
|
||||
|
||||
def _get_or_create_dept_location(self, department, company):
|
||||
"""根据部门查找或自动创建内部位置,返回 location 记录"""
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
# 优先在当前公司下按名称+内部位置查找
|
||||
location = self.env['stock.location'].search([
|
||||
('name', '=', department.name),
|
||||
('usage', '=', 'internal'),
|
||||
('company_id', '=', company.id),
|
||||
], limit=1)
|
||||
_logger.info('LOCATION DEBUG1: dept=%s, company=%s(%s), found=%s',
|
||||
department.name, company.name, company.id,
|
||||
location.complete_name if location else 'None')
|
||||
# 任何情况都找不到则创建一个属于当前公司的新位置
|
||||
if not location:
|
||||
parent_location = self.env['stock.location'].search([
|
||||
('complete_name', '=', company.name),
|
||||
], limit=1)
|
||||
location = self.env['stock.location'].create({
|
||||
'name': department.name,
|
||||
'location_id': parent_location.id if parent_location else False,
|
||||
'usage': 'internal',
|
||||
'company_id': company.id,
|
||||
})
|
||||
_logger.info('LOCATION DEBUG3: created=%s', location.complete_name)
|
||||
return location
|
||||
|
||||
@api.depends('picking_type_id', 'partner_id')
|
||||
def _compute_location_id(self):
|
||||
"""\u91cd\u5199\u8ba1\u7b97\u4f4d\u7f6e\uff1ainternal \u7c7b\u578b\u65f6\u81ea\u52a8\u7528\u5f53\u524d\u7528\u6237\u90e8\u95e8\u4f4d\u7f6e\u4f5c\u4e3a\u76ee\u7684\u4f4d\u7f6e"""
|
||||
super()._compute_location_id()
|
||||
for picking in self:
|
||||
if picking.state != 'draft' or picking.return_id:
|
||||
continue
|
||||
if picking.picking_type_code != 'internal':
|
||||
continue
|
||||
current_user = self.env.user
|
||||
employee = current_user.employee_id
|
||||
if not employee or not employee.department_id:
|
||||
continue
|
||||
company = current_user.company_id
|
||||
location = picking._get_or_create_dept_location(employee.department_id, company)
|
||||
if location:
|
||||
picking.location_dest_id = location
|
||||
|
||||
@api.onchange('picking_type_id')
|
||||
def _onchange_picking_type_auto_dest(self):
|
||||
pass # 逻辑已移至 _compute_location_id 重写
|
||||
|
||||
@api.depends(
|
||||
'move_ids_without_package.product_id',
|
||||
'move_ids_without_package.product_uom_qty',
|
||||
@@ -251,6 +338,9 @@ class StockPicking(models.Model):
|
||||
def button_validate(self):
|
||||
"""重写确认入库方法,改名为证明按鈕"""
|
||||
for record in self:
|
||||
# 借用/领用单必须已审批通过才能完成
|
||||
if record.picking_type_code == 'internal' and record.approval_state != 'approved':
|
||||
raise UserError('借用/领用单必须审批通过后才能完成')
|
||||
if record.receipt_status == 'certification':
|
||||
# 如果已指定证明人,检查当前用户是否是指定证明人
|
||||
if record.certifier_user_id and record.certifier_user_id.id != self.env.user.id:
|
||||
@@ -281,7 +371,263 @@ class StockPicking(models.Model):
|
||||
'assign_date': False,
|
||||
})
|
||||
return super(StockPicking, self).button_validate()
|
||||
|
||||
def _compute_borrow_code(self):
|
||||
for rec in self:
|
||||
wf = self.env['soong.workflow.record'].search([
|
||||
('res_model', '=', 'stock.picking'),
|
||||
('res_id', '=', rec.id),
|
||||
('reclaim_state', '=', False),
|
||||
], limit=1)
|
||||
rec.borrow_code = wf.code if wf else ''
|
||||
|
||||
def _get_or_create_workflow_record(self):
|
||||
"""获取或创建当前借用/领用单对应的 soong.workflow.record"""
|
||||
return self.env['soong.workflow.record'].search([
|
||||
('res_model', '=', 'stock.picking'),
|
||||
('res_id', '=', self.id),
|
||||
('reclaim_state', '=', False),
|
||||
], limit=1)
|
||||
|
||||
def action_submit(self):
|
||||
"""提交审批:立即扣减库存,审批人后续只影响状态"""
|
||||
for rec in self:
|
||||
if rec.picking_type_code != 'internal':
|
||||
raise UserError('只有借用/领用单才能提交审批')
|
||||
if not rec.approver_id:
|
||||
raise UserError('请先设置审批人')
|
||||
# 立即完成调拨,扣减库存
|
||||
if rec.state == 'draft':
|
||||
rec.action_confirm()
|
||||
if rec.state in ('confirmed', 'waiting'):
|
||||
rec.action_assign()
|
||||
if rec.state == 'assigned':
|
||||
# 临时设为 approved 以绕过 button_validate 拦截,完成后改回 pending
|
||||
rec.approval_state = 'approved'
|
||||
rec.button_validate()
|
||||
rec.approval_state = 'pending'
|
||||
# 固定资产:标记设备为已借出
|
||||
if rec.picking_type_id and '借用' in (rec.picking_type_id.name or ''):
|
||||
for move in rec.move_ids_without_package:
|
||||
product = move.product_id
|
||||
if '固定资产' not in product.product_tag_ids.mapped('name'):
|
||||
continue
|
||||
qty = int(move.product_uom_qty)
|
||||
if qty <= 0:
|
||||
continue
|
||||
available_equipments = self.env['maintenance.equipment'].search([
|
||||
('name', 'like', product.name + '-'),
|
||||
('borrow_state', '=', 'available'),
|
||||
], order='name asc', limit=qty)
|
||||
if len(available_equipments) < qty:
|
||||
raise UserError(
|
||||
f'产品「{product.name}」可用设备不足,'
|
||||
f'需要 {qty} 台,当前只有 {len(available_equipments)} 台可用'
|
||||
)
|
||||
available_equipments.write({'borrow_state': 'borrowed'})
|
||||
# 创建或更新 soong.workflow.record,只把审批人放入 users_ids(审批人待办)
|
||||
current_user = self.env.user
|
||||
employee = current_user.employee_id
|
||||
department = employee.department_id if employee else self.env['hr.department']
|
||||
if not department:
|
||||
department = rec.department_id
|
||||
approver_employee = rec.approver_id.employee_id if rec.approver_id else self.env['hr.employee']
|
||||
wf_name = rec.borrow_title or rec.name
|
||||
existing = rec._get_or_create_workflow_record()
|
||||
if existing:
|
||||
existing.write({
|
||||
'name': wf_name,
|
||||
'users_ids': [(6, 0, [rec.approver_id.id, current_user.id])],
|
||||
'employee_ids': [(6, 0, approver_employee.ids)],
|
||||
'done_by': current_user.id,
|
||||
'approve_state': 'pending',
|
||||
'soong_state': 'to_do',
|
||||
'reclaim_state': False,
|
||||
})
|
||||
else:
|
||||
self.env['soong.workflow.record'].sudo().create({
|
||||
'name': wf_name,
|
||||
'code': self.env['ir.sequence'].sudo().next_by_code('borrow.workflow.sequence'),
|
||||
'res_model': 'stock.picking',
|
||||
'res_id': rec.id,
|
||||
'users_ids': [(6, 0, [rec.approver_id.id, current_user.id])],
|
||||
'employee_ids': [(6, 0, approver_employee.ids)],
|
||||
'done_by': current_user.id,
|
||||
'approve_state': 'pending',
|
||||
'soong_state': 'to_do',
|
||||
'department_id': department.id if department else False,
|
||||
'workflow_id': 239,
|
||||
})
|
||||
# 申请人提交时立即创建 log:申请人已办(2),审批人待办(0)
|
||||
self.env['soong.workflow.log'].sudo().create([
|
||||
{
|
||||
'model': 'stock.picking',
|
||||
'res_id': rec.id,
|
||||
'done_by': current_user.id,
|
||||
'approval_state': '2',
|
||||
'lase_click_date': fields.Datetime.now(),
|
||||
'comment': '已提交审批',
|
||||
},
|
||||
{
|
||||
'model': 'stock.picking',
|
||||
'res_id': rec.id,
|
||||
'done_by': rec.approver_id.id,
|
||||
'approval_state': '0',
|
||||
'lase_click_date': fields.Datetime.now(),
|
||||
'comment': '待审批',
|
||||
},
|
||||
])
|
||||
|
||||
def action_approve(self):
|
||||
"""审批通过:仅更新审批状态,库存已在提交时扣减"""
|
||||
for rec in self:
|
||||
if rec.approver_id and rec.approver_id.id != self.env.user.id:
|
||||
raise UserError('只有指定的审批人才能批准')
|
||||
rec.approval_state = 'approved'
|
||||
# 更新 workflow record 为已办(结束状态)
|
||||
wf_record = rec._get_or_create_workflow_record()
|
||||
if wf_record:
|
||||
wf_record.write({
|
||||
'approve_state': 'end',
|
||||
'soong_state': 'done',
|
||||
})
|
||||
# 将审批人待办 log 更新为已办结(2)
|
||||
approver_logs = self.env['soong.workflow.log'].search([
|
||||
('model', '=', 'stock.picking'),
|
||||
('res_id', '=', rec.id),
|
||||
('done_by', '=', self.env.user.id),
|
||||
('approval_state', '=', '0'),
|
||||
])
|
||||
if approver_logs:
|
||||
approver_logs.write({'approval_state': '2', 'comment': '批准', 'lase_click_date': fields.Datetime.now()})
|
||||
else:
|
||||
self.env['soong.workflow.log'].sudo().create({
|
||||
'model': 'stock.picking',
|
||||
'res_id': rec.id,
|
||||
'done_by': self.env.user.id,
|
||||
'approval_state': '2',
|
||||
'lase_click_date': fields.Datetime.now(),
|
||||
'comment': '批准',
|
||||
})
|
||||
|
||||
def action_refuse(self):
|
||||
"""审批拒绝:创建反向调拨还原库存"""
|
||||
for rec in self:
|
||||
if rec.approver_id and rec.approver_id.id != self.env.user.id:
|
||||
raise UserError('只有指定的审批人才能拒绝')
|
||||
if rec.state != 'done':
|
||||
raise UserError('当前单据状态异常,无法拒绝')
|
||||
# 创建反向调拨单还原库存
|
||||
reverse_vals = {
|
||||
'picking_type_id': rec.picking_type_id.id,
|
||||
'location_id': rec.location_dest_id.id,
|
||||
'location_dest_id': rec.location_id.id,
|
||||
'origin': rec.name + '(拒绝还原)',
|
||||
'approval_state': 'approved',
|
||||
'move_ids': [(0, 0, {
|
||||
'name': move.name,
|
||||
'product_id': move.product_id.id,
|
||||
'product_uom_qty': move.quantity or move.product_uom_qty,
|
||||
'product_uom': move.product_uom.id,
|
||||
'location_id': rec.location_dest_id.id,
|
||||
'location_dest_id': rec.location_id.id,
|
||||
}) for move in rec.move_ids if move.state == 'done'],
|
||||
}
|
||||
reverse_picking = self.env['stock.picking'].create(reverse_vals)
|
||||
reverse_picking.action_confirm()
|
||||
reverse_picking.action_assign()
|
||||
if reverse_picking.state == 'assigned':
|
||||
reverse_picking.button_validate()
|
||||
rec.approval_state = 'refused'
|
||||
# 固定资产:恢复设备为可用
|
||||
if rec.picking_type_id and '借用' in (rec.picking_type_id.name or ''):
|
||||
for move in rec.move_ids:
|
||||
product = move.product_id
|
||||
if '固定资产' not in product.product_tag_ids.mapped('name'):
|
||||
continue
|
||||
qty = int(move.quantity or move.product_uom_qty)
|
||||
borrowed_equip = self.env['maintenance.equipment'].search([
|
||||
('name', 'like', product.name + '-'),
|
||||
('borrow_state', '=', 'borrowed'),
|
||||
], order='name asc', limit=qty)
|
||||
borrowed_equip.write({'borrow_state': 'available'})
|
||||
# 更新 workflow record 为已办(结束状态)
|
||||
wf_record = rec._get_or_create_workflow_record()
|
||||
if wf_record:
|
||||
wf_record.write({
|
||||
'approve_state': 'end',
|
||||
'soong_state': 'done',
|
||||
})
|
||||
# 将审批人待办 log 更新为已办结(2)
|
||||
approver_logs = self.env['soong.workflow.log'].search([
|
||||
('model', '=', 'stock.picking'),
|
||||
('res_id', '=', rec.id),
|
||||
('done_by', '=', self.env.user.id),
|
||||
('approval_state', '=', '0'),
|
||||
])
|
||||
if approver_logs:
|
||||
approver_logs.write({'approval_state': '2', 'comment': '拒绝', 'lase_click_date': fields.Datetime.now()})
|
||||
else:
|
||||
self.env['soong.workflow.log'].sudo().create({
|
||||
'model': 'stock.picking',
|
||||
'res_id': rec.id,
|
||||
'done_by': self.env.user.id,
|
||||
'approval_state': '2',
|
||||
'lase_click_date': fields.Datetime.now(),
|
||||
'comment': '拒绝',
|
||||
})
|
||||
|
||||
def action_approve_return(self):
|
||||
"""归还审批通过:创建反向调拨单并完成"""
|
||||
for rec in self:
|
||||
if rec.return_approver_id and rec.return_approver_id.id != self.env.user.id:
|
||||
raise UserError('只有指定的归还审批人才能批准归还')
|
||||
if rec.return_state != 'pending':
|
||||
raise UserError('当前状态不允许批准归还')
|
||||
# 创建反向调拨单(目标位置 → 源位置)
|
||||
return_picking_vals = {
|
||||
'picking_type_id': rec.picking_type_id.id,
|
||||
'location_id': rec.location_dest_id.id,
|
||||
'location_dest_id': rec.location_id.id,
|
||||
'origin': rec.name + '(归还)',
|
||||
'approval_state': 'approved',
|
||||
'move_ids': [(0, 0, {
|
||||
'name': move.name,
|
||||
'product_id': move.product_id.id,
|
||||
'product_uom_qty': move.quantity or move.product_uom_qty,
|
||||
'product_uom': move.product_uom.id,
|
||||
'location_id': rec.location_dest_id.id,
|
||||
'location_dest_id': rec.location_id.id,
|
||||
}) for move in rec.move_ids if move.state == 'done'],
|
||||
}
|
||||
return_picking = self.env['stock.picking'].create(return_picking_vals)
|
||||
return_picking.action_confirm()
|
||||
return_picking.action_assign()
|
||||
if return_picking.state == 'assigned':
|
||||
return_picking.button_validate()
|
||||
# 更新归还状态及固定资产设备状态
|
||||
rec.return_state = 'approved'
|
||||
if rec.picking_type_id and '借用' in (rec.picking_type_id.name or ''):
|
||||
for move in rec.move_ids:
|
||||
product = move.product_id
|
||||
if '固定资产' not in product.product_tag_ids.mapped('name'):
|
||||
continue
|
||||
qty = int(move.quantity or move.product_uom_qty)
|
||||
borrowed_equipments = self.env['maintenance.equipment'].search([
|
||||
('name', 'like', product.name + '-'),
|
||||
('borrow_state', '=', 'borrowed'),
|
||||
], order='name asc', limit=qty)
|
||||
borrowed_equipments.write({'borrow_state': 'available'})
|
||||
|
||||
def action_refuse_return(self):
|
||||
"""归还审批拒绝"""
|
||||
for rec in self:
|
||||
if rec.return_approver_id and rec.return_approver_id.id != self.env.user.id:
|
||||
raise UserError('只有指定的归还审批人才能拒绝归还')
|
||||
if rec.return_state != 'pending':
|
||||
raise UserError('当前状态不允许拒绝归还')
|
||||
rec.return_state = 'refused'
|
||||
|
||||
def action_refresh_json_display(self):
|
||||
"""手动刷新json_display字段"""
|
||||
for record in self:
|
||||
@@ -337,6 +683,20 @@ class StockPicking(models.Model):
|
||||
self.receipt_status = 'certification' # 状态变为证明
|
||||
return True
|
||||
|
||||
def action_reset_to_acceptance(self):
|
||||
"""管理员退回:清空验收信息,重新回到待转交验收节点"""
|
||||
self.ensure_one()
|
||||
if not self.env.user.has_group('base.group_system'):
|
||||
raise UserError('只有系统管理员才能执行此操作!')
|
||||
if self.state == 'done':
|
||||
raise UserError('已完成入库的单据不能退回,如需撤销请联系技术人员!')
|
||||
self.write({
|
||||
'receipt_status': 'acceptance',
|
||||
'acceptance_user_id': False,
|
||||
'employee_id': False,
|
||||
'acceptance_date': False,
|
||||
})
|
||||
|
||||
def action_transfer_acceptance(self):
|
||||
"""转交验收:弹出选人 wizard"""
|
||||
self.ensure_one()
|
||||
@@ -405,10 +765,38 @@ class StockPicking(models.Model):
|
||||
if picking.picking_type_code == 'incoming':
|
||||
new_name = picking._generate_sh_sequence(picking.scheduled_date)
|
||||
picking.write({'name': new_name})
|
||||
# 借用/领用单创建时自动建 workflow record,和申请人关联(草稿待办)
|
||||
elif picking.picking_type_code == 'internal':
|
||||
current_user = self.env.user
|
||||
employee = current_user.employee_id
|
||||
department = employee.department_id if employee else self.env['hr.department']
|
||||
if not department:
|
||||
department = picking.department_id
|
||||
self.env['soong.workflow.record'].sudo().create({
|
||||
'name': picking.borrow_title or picking.name,
|
||||
'code': self.env['ir.sequence'].sudo().next_by_code('borrow.workflow.sequence'),
|
||||
'res_model': 'stock.picking',
|
||||
'res_id': picking.id,
|
||||
'users_ids': [(6, 0, [current_user.id])],
|
||||
'employee_ids': [(6, 0, employee.ids)],
|
||||
'done_by': current_user.id,
|
||||
'approve_state': 'start',
|
||||
'soong_state': 'to_do',
|
||||
'department_id': department.id if department else False,
|
||||
'workflow_id': 239,
|
||||
})
|
||||
|
||||
return pickings
|
||||
|
||||
def write(self, vals):
|
||||
"""重写write方法"""
|
||||
result = super(StockPicking, self).write(vals)
|
||||
# 标题变化时同步更新 workflow record 的 name
|
||||
if 'borrow_title' in vals:
|
||||
for rec in self:
|
||||
if rec.picking_type_code != 'internal':
|
||||
continue
|
||||
wf = rec._get_or_create_workflow_record()
|
||||
if wf and vals.get('borrow_title'):
|
||||
wf.sudo().write({'name': vals['borrow_title']})
|
||||
return result
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, api
|
||||
|
||||
|
||||
class IrModelPatch(models.Model):
|
||||
_inherit = 'ir.model'
|
||||
|
||||
def _reflect_model_params(self, model):
|
||||
vals = super(IrModelPatch, self)._reflect_model_params(model)
|
||||
# 让 stock.picking 在工作流配置界面可被搜索到
|
||||
if model._name == 'stock.picking':
|
||||
vals['is_workflow'] = True
|
||||
return vals
|
||||
|
||||
|
||||
class WorkflowRecordPatch(models.Model):
|
||||
_inherit = 'soong.workflow.record'
|
||||
|
||||
@api.model
|
||||
def fix_borrow_workflow_id(self):
|
||||
"""修复借用领用单 workflow record 缺少 workflow_id 的旧数据"""
|
||||
records = self.search([
|
||||
('res_model', '=', 'stock.picking'),
|
||||
('workflow_id', '=', False),
|
||||
])
|
||||
if records:
|
||||
records.write({'workflow_id': 239})
|
||||
@@ -1,2 +1,5 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_stock_picking_transfer_wizard,stock.picking.transfer.wizard,model_stock_picking_transfer_wizard,,1,1,1,1
|
||||
access_putaway_rule_batch_update_wizard,putaway.rule.batch.update.wizard,model_putaway_rule_batch_update_wizard,,1,1,1,1
|
||||
access_stock_picking_submit_wizard,stock.picking.submit.wizard,model_stock_picking_submit_wizard,,1,1,1,1
|
||||
access_stock_picking_return_wizard,stock.picking.return.wizard,model_stock_picking_return_wizard,,1,1,1,1
|
||||
|
||||
|
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- 在产品模板"库存"页签内加上架规则跳转按钮 -->
|
||||
<record id="inherit_product_template_stock_putaway" model="ir.ui.view">
|
||||
<field name="name">inherit.product.template.stock.putaway</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="stock.product_template_form_view_procurement_button"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='inventory']" position="inside">
|
||||
<separator string="上架规则(存储到子位置)"/>
|
||||
<group>
|
||||
<button name="action_view_related_putaway_rules"
|
||||
string="查看/编辑上架规则"
|
||||
type="object"
|
||||
class="btn-secondary"
|
||||
icon="fa-random"
|
||||
context="{'invisible_handle': True, 'single_product': True}"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -5,11 +5,14 @@
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="inherit_id" ref="stock.view_picking_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='picking_type_id']" position="before">
|
||||
<field name="borrow_code" string="流水号" readonly="1" invisible="picking_type_code != 'internal'"/>
|
||||
<field name="name" string="单据名称" invisible="picking_type_code != 'internal'" placeholder="可修改将显示于待办列表" readonly="0"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='picking_type_id']" position="after">
|
||||
<field name="department_id" options="{'no_create': True}" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='location_id'][@groups='stock.group_stock_multi_locations']" position="attributes">
|
||||
<attribute name="domain">[('id', 'in', available_location_ids)]</attribute>
|
||||
<field name="picking_type_code" invisible="1"/>
|
||||
<field name="create_uid" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='picking_type_id']" position="attributes">
|
||||
<attribute name="readonly">state != 'draft' and (receipt_status in ['acceptance', 'certification'] or receipt_status == 'receipt')</attribute>
|
||||
@@ -18,8 +21,11 @@
|
||||
<attribute name="invisible">1</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='origin']" position="after">
|
||||
<field name="purchase_id" string="源单据" readonly="1" options="{'no_create': True, 'no_open': False}"/>
|
||||
<field name="purchase_id" string="源单据" readonly="1" options="{'no_create': True, 'no_open': False}" invisible="picking_type_code == 'internal'"/>
|
||||
<field name="user_id" string="采购人员"/>
|
||||
<field name="borrower_id" string="借用人" invisible="1" options="{'no_create': True}" readonly="1"/>
|
||||
<field name="return_state" string="归还状态" invisible="1" readonly="1"/>
|
||||
<field name="return_approver_id" string="归还审批人" invisible="1" options="{'no_create': True}" readonly="1"/>
|
||||
<field name="available_product_ids" invisible="1"/>
|
||||
<field name="available_location_ids" invisible="1"/>
|
||||
<field name="state" invisible="1"/>
|
||||
@@ -28,37 +34,76 @@
|
||||
<field name="acceptance_user_id" invisible="1"/>
|
||||
<field name="certifier_user_id" invisible="1"/>
|
||||
<field name="is_admin" invisible="1"/>
|
||||
<field name="approval_state" invisible="1"/>
|
||||
<field name="approver_id" invisible="1"/>
|
||||
<field name="return_state" invisible="1"/>
|
||||
<field name="return_approver_id" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='operations']//field[@name='move_ids_without_package']//field[@name='product_id']" position="attributes">
|
||||
<attribute name="options">{'no_create': True}</attribute>
|
||||
<attribute name="domain">[('id', 'in', parent.available_product_ids)]</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='operations']//field[@name='move_ids_without_package']//field[@name='product_uom'][@groups='uom.group_uom']" position="after">
|
||||
<field name="available_location_ids" invisible="1"/>
|
||||
<field name="location_id" string="所在仓库" optional="show"
|
||||
invisible="parent.picking_type_code != 'internal'"
|
||||
domain="[('id', 'in', available_location_ids)]"
|
||||
options="{'no_create': True}"/>
|
||||
<field name="borrow_type" string="借用/领用" readonly="1" optional="show"
|
||||
invisible="parent.picking_type_code != 'internal'"/>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='%(stock.act_stock_return_picking)d']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='action_confirm']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
<attribute name="invisible">picking_type_code == 'incoming' or picking_type_code == 'internal' or state != 'draft'</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='action_cancel']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
<attribute name="invisible">picking_type_code == 'incoming' or picking_type_code == 'internal'</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='action_assign']" position="attributes">
|
||||
<attribute name="invisible">picking_type_code == 'internal' or not show_check_availability</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//header" position="inside">
|
||||
<!-- 转交验收:管理员且尚未转交时可见 -->
|
||||
<!-- 提交审批:借用/领用单且草稿或已拒绝时可见,点击弹窗选审批人和借用人 -->
|
||||
<button name="%(yuthon_stock.action_stock_picking_submit_wizard)d"
|
||||
string="提交审批" type="action" class="btn-primary"
|
||||
invisible="picking_type_code == 'incoming' or approval_state not in ['draft', 'refused']"/>
|
||||
<!-- 批准:审批人且待审批时可见 -->
|
||||
<button name="action_approve" string="批准" type="object" class="btn-primary"
|
||||
invisible="picking_type_code == 'incoming' or approval_state != 'pending' or approver_id != uid"/>
|
||||
<!-- 拒绝:审批人且待审批时可见 -->
|
||||
<button name="action_refuse" string="拒绝" type="object" class="btn-secondary"
|
||||
invisible="picking_type_code == 'incoming' or approval_state != 'pending' or approver_id != uid"/>
|
||||
<!-- 申请归还:已完成的借用/领用单且未申请归还时可见 -->
|
||||
<button name="%(yuthon_stock.action_stock_picking_return_wizard)d"
|
||||
string="申请归还" type="action" class="btn-secondary"
|
||||
invisible="picking_type_code != 'internal' or state != 'done' or return_state not in ['none', 'refused'] or create_uid != uid"/>
|
||||
<!-- 批准归还:归还审批人且待审批时可见 -->
|
||||
<button name="action_approve_return" string="批准归还" type="object" class="btn-primary"
|
||||
invisible="picking_type_code != 'internal' or return_state != 'pending' or return_approver_id != uid"/>
|
||||
<!-- 拒绝归还:归还审批人且待审批时可见 -->
|
||||
<button name="action_refuse_return" string="拒绝归还" type="object" class="btn-secondary"
|
||||
invisible="picking_type_code != 'internal' or return_state != 'pending' or return_approver_id != uid"/>
|
||||
<button name="action_transfer_acceptance" string="转交验收" type="object" class="btn-secondary"
|
||||
invisible="receipt_status != 'acceptance' or acceptance_user_id != False"/>
|
||||
invisible="receipt_status != 'acceptance' or acceptance_user_id != False or picking_type_code != 'incoming'"/>
|
||||
<!-- 管理员退回至待验收:仅管理员可见,未完成入库时可用 -->
|
||||
<button name="action_reset_to_acceptance" string="退回待验收" type="object" class="btn-secondary"
|
||||
groups="base.group_system"
|
||||
invisible="picking_type_code != 'incoming' or state == 'done' or receipt_status == 'acceptance'"/>
|
||||
<!-- 验收按钮:只有指定验收人可见,且当前用户是指定人 -->
|
||||
<button name="action_acceptance" string="验收" type="object" class="btn-primary"
|
||||
invisible="receipt_status != 'acceptance' or acceptance_user_id == False or acceptance_user_id != uid"/>
|
||||
invisible="receipt_status != 'acceptance' or acceptance_user_id == False or acceptance_user_id != uid or picking_type_code != 'incoming'"/>
|
||||
<!-- 转交证明:验收人才能转交,且尚未转交证明时可见 -->
|
||||
<button name="action_transfer_certifier" string="转交证明" type="object" class="btn-secondary"
|
||||
invisible="receipt_status != 'certification' or certifier_user_id != False or acceptance_user_id != uid"/>
|
||||
invisible="receipt_status != 'certification' or certifier_user_id != False or acceptance_user_id != uid or picking_type_code != 'incoming'"/>
|
||||
|
||||
<!-- 撤回入库:管理员且入库状态时可见 -->
|
||||
<!-- 撤回入库:已隐藏 -->
|
||||
<button name="action_revoke_receipt" string="撤回入库" type="object" class="btn-secondary"
|
||||
invisible="receipt_status != 'receipt' or not is_admin"/>
|
||||
invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@name='operations']/field[@name='move_ids_without_package']" position="after">
|
||||
<group>
|
||||
<group invisible="picking_type_code != 'incoming'">
|
||||
<group>
|
||||
<field name="employee_id" string="验收人" required="1" options="{'no_create': True}" readonly="receipt_status in ['certification', 'receipt']"/>
|
||||
<field name="document_ids" widget="preview_many2many" readonly="receipt_status in ['certification', 'receipt']"/>
|
||||
@@ -76,10 +121,16 @@
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='button_validate' and @class='oe_highlight']" position="attributes">
|
||||
<attribute name="string">证明</attribute>
|
||||
<attribute name="invisible">receipt_status != 'certification' or certifier_user_id == False or certifier_user_id != uid</attribute>
|
||||
<attribute name="invisible">picking_type_code != 'incoming' or receipt_status != 'certification' or certifier_user_id == False or certifier_user_id != uid</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='location_dest_id'][@groups='stock.group_stock_multi_locations']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
<xpath expr="//button[@name='button_validate' and @class='oe_highlight']" position="after">
|
||||
<button name="button_validate" string="完成" type="object" class="btn-primary"
|
||||
invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='location_dest_id'][@groups='stock.group_stock_multi_locations']" position="replace">
|
||||
<field name="location_dest_id" options="{'no_create': True}" groups="stock.group_stock_multi_locations"
|
||||
invisible="picking_type_code != 'internal'"
|
||||
readonly="state == 'done'"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='picking_properties']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
@@ -92,19 +143,49 @@
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='user_id']" position="attributes">
|
||||
<attribute name="string">采购人员</attribute>
|
||||
<attribute name="invisible">picking_type_code == 'internal'</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='partner_id']" position="attributes">
|
||||
<attribute name="string">供应商</attribute>
|
||||
<attribute name="readonly">state != 'draft' and (receipt_status in ['acceptance', 'certification'] or receipt_status == 'receipt')</attribute>
|
||||
<attribute name="invisible">picking_type_code == 'internal'</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@class='o_td_label'][label[@for='partner_id']]" position="attributes">
|
||||
<attribute name="invisible">picking_type_code == 'internal'</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='origin'][@invisible='1']" position="attributes">
|
||||
<attribute name="invisible">1</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@class='oe_title']//field[@name='name']" position="attributes">
|
||||
<attribute name="invisible">picking_type_code == 'internal'</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@class='oe_title']//field[@name='priority']" position="attributes">
|
||||
<attribute name="invisible">picking_type_code == 'internal'</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='scheduled_date']" position="attributes">
|
||||
<attribute name="readonly">state != 'draft' and (receipt_status in ['acceptance', 'certification'] or receipt_status == 'receipt')</attribute>
|
||||
</xpath>
|
||||
<xpath expr="(//field[@name='state'][@widget='statusbar'])[1]" position="replace">
|
||||
<field name="receipt_status" widget="statusbar" statusbar_visible="acceptance,certification,receipt" invisible="picking_type_code != 'incoming'"/>
|
||||
<xpath expr="//label[@for='scheduled_date']" position="replace">
|
||||
<label for="scheduled_date" invisible="picking_type_code == 'internal'"/>
|
||||
</xpath>
|
||||
<xpath expr="//div[field[@name='scheduled_date']]" position="attributes">
|
||||
<attribute name="invisible">picking_type_code == 'internal'</attribute>
|
||||
</xpath>
|
||||
<xpath expr="(//field[@name='state'][@widget='statusbar'])[1]" position="replace">
|
||||
<field name="receipt_status" widget="statusbar" statusbar_visible="acceptance,certification,receipt" invisible="picking_type_code == 'incoming'"/>
|
||||
<field name="receipt_status" widget="statusbar" statusbar_visible="acceptance,certification,receipt" invisible="picking_type_code != 'incoming'"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,assigned,done" invisible="picking_type_code != 'internal'"/>
|
||||
</xpath>
|
||||
<xpath expr="(//field[@name='state'][@widget='statusbar'])[1]" position="replace">
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,assigned,done" invisible="picking_type_code != 'outgoing'"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='location_id'][@groups='stock.group_stock_multi_locations']" position="replace">
|
||||
<field name="location_id" options="{'no_create': True}" groups="stock.group_stock_multi_locations"
|
||||
invisible="picking_type_code == 'incoming' or picking_type_code == 'internal'"
|
||||
readonly="state == 'done'"
|
||||
domain="[('id', 'in', available_location_ids)]"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='location_id'][@groups='!stock.group_stock_multi_locations']" position="replace">
|
||||
<field name="location_id" groups="!stock.group_stock_multi_locations" invisible="1" readonly="state == 'done'"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
@@ -181,12 +262,7 @@
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="领用">
|
||||
<field name="name"/>
|
||||
<field name="partner_id" string="供应商"/>
|
||||
<field name="user_id" string="采购人员"/>
|
||||
<field name="employee_id" string="验收人"/>
|
||||
<field name="date_done"/>
|
||||
<field name="origin"/>
|
||||
<!-- <field name="json_display" widget="json_template"/>-->
|
||||
<field name="company_id"/>
|
||||
<field name="state"/>
|
||||
@@ -194,21 +270,27 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 借用出库Tree视图 -->
|
||||
<!-- 借用/领用 出库Tree视图 -->
|
||||
<record id="stock_picking_views_tree_borrow" model="ir.ui.view">
|
||||
<field name="name">stock.picking.tree.borrow</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="借用">
|
||||
<field name="name"/>
|
||||
<field name="partner_id" string="供应商"/>
|
||||
<field name="user_id" string="采购人员"/>
|
||||
<field name="employee_id" string="验收人"/>
|
||||
<field name="date_done"/>
|
||||
<field name="origin"/>
|
||||
<!-- <field name="json_display" widget="json_template"/>-->
|
||||
<field name="borrow_code" string="流水号"/>
|
||||
<field name="picking_type_id" string="类型"/>
|
||||
<field name="scheduled_date" string="调拨日期"/>
|
||||
<field name="borrower_id" string="借用人"/>
|
||||
<field name="approval_state" string="审批状态"
|
||||
decoration-info="approval_state == 'draft'"
|
||||
decoration-warning="approval_state == 'pending'"
|
||||
decoration-success="approval_state == 'approved'"
|
||||
decoration-danger="approval_state == 'refused'"/>
|
||||
<field name="return_state" string="归还状态"
|
||||
decoration-muted="return_state == 'none'"
|
||||
decoration-warning="return_state == 'pending'"
|
||||
decoration-success="return_state == 'approved'"
|
||||
decoration-danger="return_state == 'refused'"/>
|
||||
<field name="company_id"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -239,18 +321,63 @@
|
||||
<field name="view_id" ref="stock_picking_views_tree_ikuku"/>
|
||||
</record>
|
||||
|
||||
<!-- 借用/领用出库 独立 Form 视图 -->
|
||||
<record id="stock_picking_form_borrow" model="ir.ui.view">
|
||||
<field name="name">stock.picking.form.borrow</field>
|
||||
<field name="model">stock.picking</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="出库">
|
||||
<header>
|
||||
<field name="approver_id" invisible="1"/>
|
||||
<button name="action_submit" string="提交审批" type="object" class="btn-primary"
|
||||
invisible="approval_state not in ['draft', 'refused']"/>
|
||||
<button name="action_approve" string="批准" type="object" class="btn-primary"
|
||||
invisible="approval_state != 'pending' or approver_id != uid"/>
|
||||
<button name="action_refuse" string="拒绝" type="object" class="btn-secondary"
|
||||
invisible="approval_state != 'pending' or approver_id != uid"/>
|
||||
<field name="approval_state" widget="statusbar"
|
||||
statusbar_visible="draft,pending,approved,refused"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<group>
|
||||
<group string="基本信息">
|
||||
<field name="borrow_code" string="流水号" readonly="1"/>
|
||||
<field name="create_uid" string="申请人" readonly="1"/>
|
||||
<field name="approver_id" string="审批人" options="{'no_create': True}"/>
|
||||
<field name="picking_type_id" string="作业类型" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
<notebook>
|
||||
<page string="作业" name="operations">
|
||||
<field name="move_ids_without_package"
|
||||
context="{'default_picking_id': id, 'default_picking_type_id': picking_type_id}"/>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids"/>
|
||||
<field name="activity_ids"/>
|
||||
<field name="message_ids"/>
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_stock_picking_adopted" model="ir.actions.act_window">
|
||||
<field name="name">领用</field>
|
||||
<field name="res_model">stock.picking</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="stock_picking_views_tree_adopted"/>
|
||||
<field name="domain">[('picking_type_id.name', 'ilike', '领用')]</field>
|
||||
</record>
|
||||
|
||||
<record id="action_stock_picking_borrow" model="ir.actions.act_window">
|
||||
<field name="name">借用</field>
|
||||
<field name="name">借用/领用</field>
|
||||
<field name="res_model">stock.picking</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="view_id" ref="stock_picking_views_tree_borrow"/>
|
||||
<field name="domain">['&', '|', ('picking_type_id.name', 'ilike', '借用'), ('picking_type_id.name', 'ilike', '领用'), '|', ('create_uid', '=', uid), ('approver_id', '=', uid)]</field>
|
||||
<field name="context">{"default_picking_type_id": 72}</field>
|
||||
</record>
|
||||
|
||||
<record id="action_stock_picking_scrap" model="ir.actions.act_window">
|
||||
@@ -270,17 +397,19 @@
|
||||
name="出库"
|
||||
parent="stock.menu_stock_warehouse_mgmt"
|
||||
action="action_stock_picking_ikuku"
|
||||
sequence="3"/>
|
||||
sequence="3"
|
||||
active="False"/>
|
||||
<menuitem id="stock_picking_adopted"
|
||||
parent="stock.menu_stock_warehouse_mgmt"
|
||||
name="领用"
|
||||
action="action_stock_picking_adopted"
|
||||
sequence="5"/>
|
||||
sequence="5"
|
||||
active="False"/>
|
||||
<menuitem id="stock_picking_borrow"
|
||||
parent="stock.menu_stock_warehouse_mgmt"
|
||||
name="借用"
|
||||
name="出库"
|
||||
action="action_stock_picking_borrow"
|
||||
sequence="10"/>
|
||||
sequence="3"/>
|
||||
<menuitem id="stock_picking_scrap"
|
||||
parent="stock.menu_stock_warehouse_mgmt"
|
||||
name="报废"
|
||||
@@ -289,7 +418,8 @@
|
||||
|
||||
<record id="stock.action_picking_tree_incoming" model="ir.actions.act_window">
|
||||
<field name="name">收货</field>
|
||||
<field name="context">{'contact_display': 'partner_address', 'default_company_id': allowed_company_ids[0], 'restricted_picking_type_code': 'incoming', 'search_default_reception': 1, 'search_default_my_pickings': 1}</field>
|
||||
<field name="domain">['&', ('picking_type_code', '=', 'incoming'), '|', '|', ('create_uid', '=', uid), ('acceptance_user_id', '=', uid), ('certifier_user_id', '=', uid)]</field>
|
||||
<field name="context">{'contact_display': 'partner_address', 'restricted_picking_type_code': 'incoming', 'search_default_reception': 1}</field>
|
||||
</record>
|
||||
|
||||
<record id="inherit_view_picking_internal_search_my" model="ir.ui.view">
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import stock_picking_transfer_wizard
|
||||
from . import putaway_rule_batch_update_wizard
|
||||
from . import stock_picking_submit_wizard
|
||||
from . import stock_picking_return_wizard
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
class PutawayRuleBatchUpdateWizard(models.TransientModel):
|
||||
_name = 'putaway.rule.batch.update.wizard'
|
||||
_description = '批量修改上架规则目标位置'
|
||||
|
||||
location_out_id = fields.Many2one(
|
||||
'stock.location',
|
||||
string='存储到子位置',
|
||||
required=True,
|
||||
domain=[('usage', '=', 'internal')],
|
||||
options="{'no_create': True}",
|
||||
)
|
||||
|
||||
def action_confirm(self):
|
||||
rule_ids = self.env.context.get('active_ids', [])
|
||||
if rule_ids:
|
||||
self.env['stock.putaway.rule'].browse(rule_ids).write({
|
||||
'location_out_id': self.location_out_id.id,
|
||||
})
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<!-- 向导 Form 视图 -->
|
||||
<record id="putaway_rule_batch_update_wizard_form" model="ir.ui.view">
|
||||
<field name="name">putaway.rule.batch.update.wizard.form</field>
|
||||
<field name="model">putaway.rule.batch.update.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="批量修改存储到子位置">
|
||||
<group>
|
||||
<field name="location_out_id" options="{'no_create': True}"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_confirm" string="确认" type="object" class="btn-primary"/>
|
||||
<button string="取消" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Server Action:在上架规则列表绑定批量操作 -->
|
||||
<record id="action_putaway_rule_batch_update" model="ir.actions.server">
|
||||
<field name="name">批量修改存储到子位置</field>
|
||||
<field name="model_id" ref="stock.model_stock_putaway_rule"/>
|
||||
<field name="binding_model_id" ref="stock.model_stock_putaway_rule"/>
|
||||
<field name="binding_view_types">list</field>
|
||||
<field name="state">code</field>
|
||||
<field name="code">
|
||||
action = {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': '批量修改存储到子位置',
|
||||
'res_model': 'putaway.rule.batch.update.wizard',
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
'context': {'active_ids': env.context.get('active_ids', [])},
|
||||
}
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,41 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class StockPickingReturnWizard(models.TransientModel):
|
||||
_name = 'stock.picking.return.wizard'
|
||||
_description = '申请归还向导'
|
||||
|
||||
picking_id = fields.Many2one('stock.picking', string='单据', required=True)
|
||||
return_approver_id = fields.Many2one(
|
||||
'res.users',
|
||||
string='归还审批人',
|
||||
required=True,
|
||||
options="{'no_create': True}",
|
||||
)
|
||||
note = fields.Text(string='归还备注')
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
res = super().default_get(fields_list)
|
||||
picking_id = self.env.context.get('active_id')
|
||||
if picking_id:
|
||||
picking = self.env['stock.picking'].browse(picking_id)
|
||||
res['picking_id'] = picking_id
|
||||
res['return_approver_id'] = picking.return_approver_id.id or False
|
||||
return res
|
||||
|
||||
def action_confirm(self):
|
||||
picking = self.picking_id
|
||||
if not picking:
|
||||
raise UserError('未找到对应单据')
|
||||
if picking.state != 'done':
|
||||
raise UserError('只有已完成的借用单才能申请归还')
|
||||
if picking.return_state == 'approved':
|
||||
raise UserError('该借用单已归还,不能重复申请')
|
||||
picking.write({
|
||||
'return_approver_id': self.return_approver_id.id,
|
||||
'return_state': 'pending',
|
||||
})
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- 申请归还向导视图 -->
|
||||
<record id="view_stock_picking_return_wizard_form" model="ir.ui.view">
|
||||
<field name="name">stock.picking.return.wizard.form</field>
|
||||
<field name="model">stock.picking.return.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="申请归还">
|
||||
<group>
|
||||
<field name="picking_id" invisible="1"/>
|
||||
<field name="return_approver_id" options="{'no_create': True}"/>
|
||||
<field name="note"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_confirm" string="申请归还" type="object" class="btn-primary"/>
|
||||
<button string="取消" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 申请归还 Action -->
|
||||
<record id="action_stock_picking_return_wizard" model="ir.actions.act_window">
|
||||
<field name="name">申请归还</field>
|
||||
<field name="res_model">stock.picking.return.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'active_id': active_id}</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,44 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class StockPickingSubmitWizard(models.TransientModel):
|
||||
_name = 'stock.picking.submit.wizard'
|
||||
_description = '借用/领用提交审批向导'
|
||||
|
||||
picking_id = fields.Many2one('stock.picking', string='单据', required=True)
|
||||
approver_id = fields.Many2one(
|
||||
'res.users',
|
||||
string='审批人',
|
||||
required=True,
|
||||
options="{'no_create': True}",
|
||||
)
|
||||
borrower_id = fields.Many2one(
|
||||
'res.users',
|
||||
string='借用人',
|
||||
required=True,
|
||||
options="{'no_create': True}",
|
||||
)
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields_list):
|
||||
res = super().default_get(fields_list)
|
||||
picking_id = self.env.context.get('active_id')
|
||||
if picking_id:
|
||||
picking = self.env['stock.picking'].browse(picking_id)
|
||||
res['picking_id'] = picking_id
|
||||
res['approver_id'] = picking.approver_id.id or False
|
||||
res['borrower_id'] = picking.borrower_id.id or self.env.user.id
|
||||
return res
|
||||
|
||||
def action_confirm(self):
|
||||
picking = self.picking_id
|
||||
if not picking:
|
||||
raise UserError('未找到对应单据')
|
||||
picking.write({
|
||||
'approver_id': self.approver_id.id,
|
||||
'borrower_id': self.borrower_id.id,
|
||||
})
|
||||
picking.action_submit()
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- 借用/领用提交审批向导视图 -->
|
||||
<record id="view_stock_picking_submit_wizard_form" model="ir.ui.view">
|
||||
<field name="name">stock.picking.submit.wizard.form</field>
|
||||
<field name="model">stock.picking.submit.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="提交审批">
|
||||
<group>
|
||||
<field name="picking_id" invisible="1"/>
|
||||
<field name="borrower_id" options="{'no_create': True}"/>
|
||||
<field name="approver_id" options="{'no_create': True}"/>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_confirm" string="提交审批" type="object" class="btn-primary"/>
|
||||
<button string="取消" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- 提交审批 Action -->
|
||||
<record id="action_stock_picking_submit_wizard" model="ir.actions.act_window">
|
||||
<field name="name">提交审批</field>
|
||||
<field name="res_model">stock.picking.submit.wizard</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="target">new</field>
|
||||
<field name="context">{'active_id': active_id}</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -10,12 +10,10 @@ import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
def get_is_leader_domain(is_leader: bool):
|
||||
operator = 'in' if is_leader else 'not in'
|
||||
return [('user_role_ids.name', operator, ['董事长', '分管领导', '总经理', '部门副经理', '副总经理', '司机'])]
|
||||
|
||||
|
||||
class YuthonWillTask(http.Controller):
|
||||
|
||||
@http.route('/yuthon/yuthon/will/task', methods=['POST'], type='http', auth='none', csrf=False)
|
||||
@@ -87,6 +85,9 @@ class YuthonWillTask(http.Controller):
|
||||
start = int(kwargs.get('start'))
|
||||
users_id = int(kwargs.get('users_id'))
|
||||
_filter = kwargs.get('filter')
|
||||
state = kwargs.get('state')
|
||||
order = 'start_date desc' if state == 'done' else 'start_date asc'
|
||||
|
||||
domain = [('users_ids', 'in', [users_id])]
|
||||
logger.info(f"{kwargs}")
|
||||
logger.info(f"{kwargs}")
|
||||
@@ -104,11 +105,12 @@ class YuthonWillTask(http.Controller):
|
||||
end_date = start_date + timedelta(days=6)
|
||||
domain = expression.AND([domain, [('start_date', '>=', start_date), ('start_date', '<=', end_date)]])
|
||||
|
||||
domain = expression.AND([domain, [('task_state', '=', state)]])
|
||||
WillTask = request.env['yuthon.will.task'].sudo()
|
||||
request_hour_selection = WillTask.fields_get(allfields=['request_hour_from'])['request_hour_from']['selection']
|
||||
state_dict = dict(WillTask.fields_get(allfields=['task_state'])['task_state']['selection'])
|
||||
result_list = []
|
||||
task_ids = request.env['yuthon.will.task'].sudo().search(domain, offset=start, limit=20, order='start_date desc')
|
||||
task_ids = request.env['yuthon.will.task'].sudo().search(domain, offset=start, limit=20, order=order)
|
||||
for task in task_ids:
|
||||
weekday_str = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"][task.start_date.weekday()]
|
||||
result = {
|
||||
|
||||
@@ -11,6 +11,7 @@ class YuthonWillTask(models.Model):
|
||||
_name = 'yuthon.will.task'
|
||||
_description = '日程'
|
||||
_rec_name = 'name'
|
||||
_order = 'task_datetime desc'
|
||||
|
||||
time_list = [
|
||||
('0', '0:00'), ('0.25', '0:15'), ('0.5', '0:30'), ('0.75', '0:45'),
|
||||
@@ -70,6 +71,7 @@ class YuthonWillTask(models.Model):
|
||||
start_date = fields.Date(string='开始日期', default=fields.Date.today)
|
||||
end_date = fields.Date(string='结束日期', default=fields.Date.today)
|
||||
task_time = fields.Char(string='任务时间', compute='_compute_task_time', store=True)
|
||||
task_datetime = fields.Datetime(string='任务时间排序', compute='_compute_task_datetime', store=True)
|
||||
is_this_company = fields.Boolean(string='本公司', default=True)
|
||||
task_state = fields.Selection([('will', '待办'), ('done', '已办'), ('cancel', '取消')], default='will', string='状态', store=True)
|
||||
|
||||
@@ -171,6 +173,27 @@ class YuthonWillTask(models.Model):
|
||||
for record in will_ids:
|
||||
record._compute_task_time()
|
||||
|
||||
@api.depends('start_date', 'request_hour_from')
|
||||
def _compute_task_datetime(self):
|
||||
"""将 start_date + request_hour_from 拼合为 Datetime,用于排序"""
|
||||
for record in self:
|
||||
if record.start_date and record.request_hour_from:
|
||||
try:
|
||||
hour_float = float(record.request_hour_from)
|
||||
hours = int(hour_float)
|
||||
minutes = int(round((hour_float - hours) * 60))
|
||||
# 存储时转为UTC(界面显示是UTC+8,存储需减8小时)
|
||||
record.task_datetime = datetime(
|
||||
record.start_date.year,
|
||||
record.start_date.month,
|
||||
record.start_date.day,
|
||||
hours, minutes, 0
|
||||
) - timedelta(hours=8)
|
||||
except Exception:
|
||||
record.task_datetime = False
|
||||
else:
|
||||
record.task_datetime = False
|
||||
|
||||
@api.depends('start_date', 'task_week', 'request_hour_from', 'task_type', 'room_booking_id', 'room_booking_id.start_datetime', 'room_booking_id.stop_datetime')
|
||||
def _compute_task_time(self):
|
||||
for record in self:
|
||||
|
||||
@@ -152,7 +152,8 @@
|
||||
decoration-danger="priority == 'very_urgent'"
|
||||
decoration-info="priority == 'urgent'"
|
||||
decoration-success="priority == 'important'" create="false" edit="false"
|
||||
js_class="custom_task_tree_button" limit="15">
|
||||
js_class="custom_task_tree_button" limit="15" default_order="task_datetime desc">
|
||||
<field name='task_datetime' column_invisible="1"/>
|
||||
<field name='start_date' column_invisible="1"/>
|
||||
<field name='task_time'/>
|
||||
<field name='name' required="1"/>
|
||||
|
||||
@@ -38,8 +38,7 @@
|
||||
<menuitem id="inventory_operation_root" name="作业" sequence="10">
|
||||
<menuitem id="inventory_operation_in_root" name="入库" action="stock.action_picking_tree_incoming" sequence="10"/>
|
||||
<!-- <menuitem id="inventory_operation_out_root" name="出库" action="stock.action_picking_tree_outgoing" sequence="20"/>-->
|
||||
<menuitem id="inventory_operation_borrow_root" name="借用" action="stock.action_picking_tree_internal" sequence="30"/>
|
||||
<menuitem id="inventory_operation_allocate_root" name="领用" action="stock.action_picking_tree_internal" sequence="40"/>
|
||||
<menuitem id="inventory_operation_borrow_root" name="出库" action="yuthon_stock.action_stock_picking_borrow" sequence="30"/>
|
||||
</menuitem>
|
||||
<menuitem id="inventory_asset_root" name="资产" sequence="30">
|
||||
<menuitem id="inventory_asset_product_root" name="产品" sequence="10">
|
||||
|
||||
Reference in New Issue
Block a user